am 07023777: am 9f71025b: Merge "Add emergency call button to PUK\'d lockscreen." into froyo

Merge commit '070237771e89bbf027ed85302df7a91fae0fa5c2' into kraken

* commit '070237771e89bbf027ed85302df7a91fae0fa5c2':
  Add emergency call button to PUK'd lockscreen.
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 707404b..6455103 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,6 +51,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/app)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/FrameworkTest_intermediates/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.policy*)
+$(call add-clean-step, rm -rf $(TARGET_OUT_JAVA_LIBRARIES)/android.policy.jar)
 
 
 # ************************************************
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 41f070c..b8ba3f6 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -16,7 +16,7 @@
 
 #include "installd.h"
 
-int install(const char *pkgname, uid_t uid, gid_t gid)
+int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid)
 {
     char pkgdir[PKG_PATH_MAX];
     char libdir[PKG_PATH_MAX];
@@ -27,11 +27,17 @@
         
     }
 
-    if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
-        return -1;
-    if (create_pkg_path(libdir, PKG_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX))
-        return -1;
-
+    if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+        if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+            return -1;
+        if (create_pkg_path(libdir, PKG_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX))
+            return -1;
+    } else {
+        if (create_pkg_path(pkgdir, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+            return -1;
+        if (create_pkg_path(libdir, PKG_SEC_LIB_PREFIX, pkgname, PKG_LIB_POSTFIX))
+            return -1;
+    }
 
     if (mkdir(pkgdir, 0751) < 0) {
         LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
@@ -56,27 +62,38 @@
     return 0;
 }
 
-int uninstall(const char *pkgname)
+int uninstall(const char *pkgname, int encrypted_fs_flag)
 {
     char pkgdir[PKG_PATH_MAX];
 
-    if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
-        return -1;
+    if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+        if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+            return -1;
+    } else {
+        if (create_pkg_path(pkgdir, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+            return -1;
+    }
 
         /* delete contents AND directory, no exceptions */
     return delete_dir_contents(pkgdir, 1, 0);
 }
 
-int renamepkg(const char *oldpkgname, const char *newpkgname)
+int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag)
 {
     char oldpkgdir[PKG_PATH_MAX];
     char newpkgdir[PKG_PATH_MAX];
 
-    if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
-        return -1;
-    if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
-        return -1;
-
+    if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+        if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+            return -1;
+        if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+            return -1;
+    } else {
+        if (create_pkg_path(oldpkgdir, PKG_SEC_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+            return -1;
+        if (create_pkg_path(newpkgdir, PKG_SEC_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+            return -1;
+    }
 
     if (rename(oldpkgdir, newpkgdir) < 0) {
         LOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno));
@@ -85,29 +102,41 @@
     return 0;
 }
 
-int delete_user_data(const char *pkgname)
+int delete_user_data(const char *pkgname, int encrypted_fs_flag)
 {
     char pkgdir[PKG_PATH_MAX];
 
-    if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
-        return -1;
+    if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+        if (create_pkg_path(pkgdir, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+            return -1;
+    } else {
+        if (create_pkg_path(pkgdir, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX))
+            return -1;
+    }
 
         /* delete contents, excluding "lib", but not the directory itself */
     return delete_dir_contents(pkgdir, 0, "lib");
 }
 
-int delete_cache(const char *pkgname)
+int delete_cache(const char *pkgname, int encrypted_fs_flag)
 {
     char cachedir[PKG_PATH_MAX];
 
-    if (create_pkg_path(cachedir, CACHE_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX))
-        return -1;
-
+    if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+        if (create_pkg_path(cachedir, CACHE_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX))
+            return -1;
+    } else {
+        if (create_pkg_path(cachedir, CACHE_SEC_DIR_PREFIX, pkgname, CACHE_DIR_POSTFIX))
+            return -1;
+    }
 
         /* delete contents, not the directory, no exceptions */
     return delete_dir_contents(cachedir, 0, 0);
 }
 
+/* TODO(oam): depending on use case (ecryptfs or dmcrypt)
+ * change implementation
+ */
 static int disk_free()
 {
     struct statfs sfs;
@@ -139,6 +168,39 @@
     LOGI("free_cache(%d) avail %d\n", free_size, avail);
     if (avail >= free_size) return 0;
 
+    /* First try encrypted dir */
+    d = opendir(PKG_SEC_DIR_PREFIX);
+    if (d == NULL) {
+        LOGE("cannot open %s\n", PKG_SEC_DIR_PREFIX);
+    } else {
+        dfd = dirfd(d);
+
+        while ((de = readdir(d))) {
+           if (de->d_type != DT_DIR) continue;
+           name = de->d_name;
+
+            /* always skip "." and ".." */
+            if (name[0] == '.') {
+                if (name[1] == 0) continue;
+                if ((name[1] == '.') && (name[2] == 0)) continue;
+            }
+
+            subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+            if (subfd < 0) continue;
+
+            delete_dir_contents_fd(subfd, "cache");
+            close(subfd);
+
+            avail = disk_free();
+            if (avail >= free_size) {
+                closedir(d);
+                return 0;
+            }
+        }
+        closedir(d);
+    }
+
+    /* Next try unencrypted dir... */
     d = opendir(PKG_DIR_PREFIX);
     if (d == NULL) {
         LOGE("cannot open %s\n", PKG_DIR_PREFIX);
@@ -314,7 +376,7 @@
 
 int get_size(const char *pkgname, const char *apkpath,
              const char *fwdlock_apkpath,
-             int *_codesize, int *_datasize, int *_cachesize)
+             int *_codesize, int *_datasize, int *_cachesize, int encrypted_fs_flag)
 {
     DIR *d;
     int dfd;
@@ -349,8 +411,14 @@
         }
     }
 
-    if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) {
-        goto done;
+    if (encrypted_fs_flag == 0) {
+        if (create_pkg_path(path, PKG_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) {
+            goto done;
+        }
+    } else {
+        if (create_pkg_path(path, PKG_SEC_DIR_PREFIX, pkgname, PKG_DIR_POSTFIX)) {
+            goto done;
+        }
     }
 
     d = opendir(path);
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index f6ca998..882c493 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -29,7 +29,7 @@
 
 static int do_install(char **arg, char reply[REPLY_MAX])
 {
-    return install(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */
+    return install(arg[0], atoi(arg[1]), atoi(arg[2]), atoi(arg[3])); /* pkgname, uid, gid */
 }
 
 static int do_dexopt(char **arg, char reply[REPLY_MAX])
@@ -50,12 +50,12 @@
 
 static int do_remove(char **arg, char reply[REPLY_MAX])
 {
-    return uninstall(arg[0]); /* pkgname */
+    return uninstall(arg[0], atoi(arg[1])); /* pkgname */
 }
 
 static int do_rename(char **arg, char reply[REPLY_MAX])
 {
-    return renamepkg(arg[0], arg[1]); /* oldpkgname, newpkgname */
+    return renamepkg(arg[0], arg[1], atoi(arg[2])); /* oldpkgname, newpkgname */
 }
 
 static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_size */
@@ -65,7 +65,7 @@
 
 static int do_rm_cache(char **arg, char reply[REPLY_MAX])
 {
-    return delete_cache(arg[0]); /* pkgname */
+    return delete_cache(arg[0], atoi(arg[1])); /* pkgname */
 }
 
 static int do_protect(char **arg, char reply[REPLY_MAX])
@@ -81,7 +81,7 @@
     int res = 0;
 
         /* pkgdir, apkpath */
-    res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize);
+    res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize, atoi(arg[3]));
 
     sprintf(reply,"%d %d %d", codesize, datasize, cachesize);
     return res;
@@ -89,7 +89,7 @@
 
 static int do_rm_user_data(char **arg, char reply[REPLY_MAX])
 {
-    return delete_user_data(arg[0]); /* pkgname */
+    return delete_user_data(arg[0], atoi(arg[1])); /* pkgname */
 }
 
 static int do_movefiles(char **arg, char reply[REPLY_MAX])
@@ -105,17 +105,17 @@
 
 struct cmdinfo cmds[] = {
     { "ping",                 0, do_ping },
-    { "install",              3, do_install },
+    { "install",              4, do_install },
     { "dexopt",               3, do_dexopt },
     { "movedex",              2, do_move_dex },
     { "rmdex",                1, do_rm_dex },
-    { "remove",               1, do_remove },
-    { "rename",               2, do_rename },
+    { "remove",               2, do_remove },
+    { "rename",               3, do_rename },
     { "freecache",            1, do_free_cache },
-    { "rmcache",              1, do_rm_cache },
+    { "rmcache",              2, do_rm_cache },
     { "protect",              2, do_protect },
-    { "getsize",              3, do_get_size },
-    { "rmuserdata",           1, do_rm_user_data },
+    { "getsize",              4, do_get_size },
+    { "rmuserdata",           2, do_rm_user_data },
     { "movefiles",            0, do_movefiles },
 };
 
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index cfcdb98..8e4adb1 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -48,16 +48,23 @@
 /* elements combined with a valid package name to form paths */
 
 #define PKG_DIR_PREFIX         "/data/data/"
+#define PKG_SEC_DIR_PREFIX     "/data/secure/data/"
 #define PKG_DIR_POSTFIX        ""
 
 #define PKG_LIB_PREFIX         "/data/data/"
+#define PKG_SEC_LIB_PREFIX     "/data/secure/data/"
 #define PKG_LIB_POSTFIX        "/lib"
 
 #define CACHE_DIR_PREFIX       "/data/data/"
+#define CACHE_SEC_DIR_PREFIX   "/data/secure/data/"
 #define CACHE_DIR_POSTFIX      "/cache"
 
 #define APK_DIR_PREFIX         "/data/app/"
 
+/* Encrypted File SYstems constants */
+#define USE_ENCRYPTED_FS       1
+#define USE_UNENCRYPTED_FS     0
+
 /* other handy constants */
 
 #define PROTECTED_DIR_PREFIX  "/data/app-private/"
@@ -89,16 +96,16 @@
 
 /* commands.c */
 
-int install(const char *pkgname, uid_t uid, gid_t gid);
-int uninstall(const char *pkgname);
-int renamepkg(const char *oldpkgname, const char *newpkgname);
-int delete_user_data(const char *pkgname);
-int delete_cache(const char *pkgname);
+int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid);
+int uninstall(const char *pkgname, int encrypted_fs_flag);
+int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag);
+int delete_user_data(const char *pkgname, int encrypted_fs_flag);
+int delete_cache(const char *pkgname, int encrypted_fs_flag);
 int move_dex(const char *src, const char *dst);
 int rm_dex(const char *path);
 int protect(char *pkgname, gid_t gid);
 int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath,
-             int *codesize, int *datasize, int *cachesize);
+             int *codesize, int *datasize, int *cachesize, int encrypted_fs_flag);
 int free_cache(int free_size);
 int dexopt(const char *apk_path, uid_t uid, int is_public);
 int movefiles();
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 1cd7aa7..1d9e0f1 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1483,7 +1483,13 @@
     }
 
     private static String getDatabaseName() {
-        return DATABASE_NAME;
+        if(Environment.isEncryptedFilesystemEnabled()) {
+            // Hard-coded path in case of encrypted file system
+            return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME;
+        } else {
+            // Regular path in case of non-encrypted file system
+            return DATABASE_NAME;
+        }
     }
 
     private class DatabaseHelper extends SQLiteOpenHelper {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 98a4993..6413cec 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -317,7 +317,9 @@
         if (sSyncStorageEngine != null) {
             return;
         }
-        File dataDir = Environment.getDataDirectory();
+        // This call will return the correct directory whether Encrypted File Systems is
+        // enabled or not.
+        File dataDir = Environment.getSecureDataDirectory();
         sSyncStorageEngine = new SyncStorageEngine(context, dataDir);
     }
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0a04e5b..023f9c4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -252,6 +252,16 @@
     public static final int FLAG_RESTORE_ANY_VERSION = 1<<17;
 
     /**
+     * Value for {@link #flags}: this is true if the application has set
+     * its android:neverEncrypt to true, false otherwise. It is used to specify
+     * that this package specifically "opts-out" of a secured file system solution,
+     * and will always store its data in-the-clear.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_NEVER_ENCRYPT = 1<<18;
+
+    /**
      * Value for {@link #flags}: Set to true if the application has been
      * installed using the forward lock option.
      *
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2a20a2d..f9e12ce 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1542,6 +1542,12 @@
             ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
         }
 
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_neverEncrypt,
+                false)) {
+            ai.flags |= ApplicationInfo.FLAG_NEVER_ENCRYPT;
+        }
+
         String str;
         str = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 812391c..c7cbed6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -28,6 +28,8 @@
     private static final File ROOT_DIRECTORY
             = getDirectory("ANDROID_ROOT", "/system");
 
+    private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+
     private static IMountService mMntSvc = null;
 
     /**
@@ -37,9 +39,55 @@
         return ROOT_DIRECTORY;
     }
 
+    /**
+     * Gets the system directory available for secure storage.
+     * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
+     * Otherwise, it returns the unencrypted /data/system directory.
+     * @return File object representing the secure storage system directory.
+     * @hide
+     */
+    public static File getSystemSecureDirectory() {
+        if (isEncryptedFilesystemEnabled()) {
+            return new File(SECURE_DATA_DIRECTORY, "system");
+        } else {
+            return new File(DATA_DIRECTORY, "system");
+        }
+    }
+
+    /**
+     * Gets the data directory for secure storage.
+     * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure).
+     * Otherwise, it returns the unencrypted /data directory.
+     * @return File object representing the data directory for secure storage.
+     * @hide
+     */
+    public static File getSecureDataDirectory() {
+        if (isEncryptedFilesystemEnabled()) {
+            return SECURE_DATA_DIRECTORY;
+        } else {
+            return DATA_DIRECTORY;
+        }
+    }
+
+    /**
+     * Returns whether the Encrypted File System feature is enabled on the device or not.
+     * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
+     * if disabled.
+     * @hide
+     */
+    public static boolean isEncryptedFilesystemEnabled() {
+        return SystemProperties.getBoolean(SYSTEM_PROPERTY_EFS_ENABLED, false);
+    }
+
     private static final File DATA_DIRECTORY
             = getDirectory("ANDROID_DATA", "/data");
 
+    /**
+     * @hide
+     */
+    private static final File SECURE_DATA_DIRECTORY
+            = getDirectory("ANDROID_SECURE_DATA", "/data/secure");
+
     private static final File EXTERNAL_STORAGE_DIRECTORY
             = getDirectory("EXTERNAL_STORAGE", "/sdcard");
 
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index b3ec114..1b103aa 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -352,6 +352,23 @@
     }
 
     /**
+     * Reboot into the recovery system to wipe the /data partition and toggle
+     * Encrypted File Systems on/off.
+     * @param extras to add to the RECOVERY_COMPLETED intent after rebooting.
+     * @throws IOException if something goes wrong.
+     *
+     * @hide
+     */
+    public static void rebootToggleEFS(Context context, boolean efsEnabled)
+        throws IOException {
+        if (efsEnabled) {
+            bootCommand(context, "--set_encrypted_filesystem=on");
+        } else {
+            bootCommand(context, "--set_encrypted_filesystem=off");
+        }
+    }
+
+    /**
      * Reboot into the recovery system with the supplied argument.
      * @param arg to pass to the recovery utility.
      * @throws IOException if something goes wrong.
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 24818a8..03b0957 100755
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -30,7 +30,6 @@
 import android.widget.Toast;
 import android.util.Log;
 import android.location.LocationManager;
-import com.android.internal.location.GpsLocationProvider;
 import com.android.internal.location.GpsNetInitiatedHandler;
 
 /**
diff --git a/core/java/com/android/internal/widget/DigitalClock.java b/core/java/com/android/internal/widget/DigitalClock.java
index 23e2277..fa47ff6 100644
--- a/core/java/com/android/internal/widget/DigitalClock.java
+++ b/core/java/com/android/internal/widget/DigitalClock.java
@@ -30,7 +30,7 @@
 import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import java.text.DateFormatSymbols;
@@ -39,7 +39,7 @@
 /**
  * Displays the time
  */
-public class DigitalClock extends RelativeLayout {
+public class DigitalClock extends LinearLayout {
 
     private final static String M12 = "h:mm";
     private final static String M24 = "kk:mm";
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 85d1a6f..6d1a414 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -121,7 +121,6 @@
 	android_server_BluetoothA2dpService.cpp \
 	android_message_digest_sha1.cpp \
 	android_ddm_DdmHandleNativeHeap.cpp \
-	android_location_GpsLocationProvider.cpp \
 	com_android_internal_os_ZygoteInit.cpp \
 	com_android_internal_graphics_NativeUtils.cpp \
 	android_backup_BackupDataInput.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7f8e854..c9e5bdc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -154,7 +154,6 @@
 extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
-extern int register_android_location_GpsLocationProvider(JNIEnv* env);
 extern int register_android_backup_BackupDataInput(JNIEnv *env);
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
@@ -1267,7 +1266,6 @@
     REG_JNI(register_android_server_BluetoothA2dpService),
     REG_JNI(register_android_message_digest_sha1),
     REG_JNI(register_android_ddm_DdmHandleNativeHeap),
-    REG_JNI(register_android_location_GpsLocationProvider),
     REG_JNI(register_android_backup_BackupDataInput),
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelperBase),
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index 945d283..04bc693 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -41,21 +41,20 @@
         android:ellipsize="marquee"
         android:gravity="right|bottom"
         android:textAppearance="?android:attr/textAppearanceMedium"
-        android:textSize="22sp"
         />
 
-    <!-- "emergency calls only" shown when sim is missing or PUKd -->
-    <TextView
-        android:id="@+id/emergencyCallText"
+    <!-- emergency call button shown when sim is missing or PUKd -->
+    <Button
+        android:id="@+id/emergencyCallButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_below="@id/carrier"
+        android:layout_alignParentTop="true"
         android:layout_alignParentRight="true"
-        android:layout_marginTop="0dip"
+        android:layout_marginTop="10dip"
         android:layout_marginRight="8dip"
-        android:text="@string/emergency_calls_only"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="@color/white"
+        android:drawableLeft="@drawable/ic_emergency"
+        style="@style/Widget.Button.Transparent"
+        android:drawablePadding="8dip"
        />
 
     <!-- time and date -->
@@ -65,7 +64,6 @@
         android:layout_below="@id/carrier"
         android:layout_marginTop="52dip"
         android:layout_marginLeft="20dip"
-        android:layout_marginBottom="8dip"
         >
 
         <TextView android:id="@+id/timeDisplay"
@@ -73,6 +71,7 @@
             android:layout_height="wrap_content"
             android:singleLine="true"
             android:ellipsize="none"
+            android:gravity="bottom"
             android:textSize="72sp"
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:shadowColor="#C0000000"
@@ -85,9 +84,8 @@
 
         <TextView android:id="@+id/am_pm"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_toRightOf="@id/timeDisplay"
-            android:layout_alignBaseline="@id/timeDisplay"
+            android:layout_height="match_parent"
+            android:gravity="bottom"
             android:singleLine="true"
             android:ellipsize="none"
             android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 6b76004e..4774dfc 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -46,19 +46,18 @@
             android:ellipsize="marquee"
             android:gravity="right|bottom"
             android:textAppearance="?android:attr/textAppearanceMedium"
-            android:textSize="22sp"
             />
 
-        <!-- "emergency calls only" shown when sim is missing or PUKd -->
-        <TextView
-            android:id="@+id/emergencyCallText"
+        <!-- emergency call button shown when sim is missing or PUKd -->
+        <Button
+            android:id="@+id/emergencyCallButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
             android:layout_marginTop="20dip"
-            android:text="@string/emergency_calls_only"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="@color/white"
+            android:drawableLeft="@drawable/ic_emergency"
+            style="@style/Widget.Button.Transparent"
+            android:drawablePadding="8dip"
            />
 
         <com.android.internal.widget.DigitalClock android:id="@+id/time"
@@ -66,12 +65,12 @@
             android:layout_height="wrap_content"
             android:layout_below="@id/carrier"
             android:layout_marginTop="56dip"
-            android:layout_marginBottom="8dip"
             >
 
             <TextView android:id="@+id/timeDisplay"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="72sp"
@@ -86,9 +85,8 @@
 
             <TextView android:id="@+id/am_pm"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toRightOf="@id/timeDisplay"
-                android:layout_alignBaseline="@id/timeDisplay"
+                android:layout_height="match_parent"
+                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index 83381a1..c1b406f 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -58,19 +58,18 @@
             android:ellipsize="marquee"
             android:gravity="right|bottom"
             />
-
         <com.android.internal.widget.DigitalClock android:id="@+id/time"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
             android:layout_alignParentLeft="true"
             android:layout_marginTop="8dip"
-            android:layout_marginBottom="8dip"
             >
 
             <TextView android:id="@+id/timeDisplay"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="72sp"
@@ -85,9 +84,8 @@
 
             <TextView android:id="@+id/am_pm"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toRightOf="@id/timeDisplay"
-                android:layout_alignBaseline="@id/timeDisplay"
+                android:layout_height="match_parent"
+                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="22sp"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 8dacfaf..74a0eee 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -55,12 +55,12 @@
             android:layout_alignParentTop="true"
             android:layout_marginTop="15dip"
             android:layout_marginLeft="20dip"
-            android:layout_marginBottom="8dip"
             >
 
             <TextView android:id="@+id/timeDisplay"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="56sp"
@@ -74,9 +74,8 @@
 
             <TextView android:id="@+id/am_pm"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_toRightOf="@id/timeDisplay"
-                android:layout_alignBaseline="@id/timeDisplay"
+                android:layout_height="match_parent"
+                android:gravity="bottom"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:textSize="18sp"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3585bf1..65ec2f7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -79,6 +79,13 @@
          by applications. -->
     <attr name="allowClearUserData" format="boolean" />
 
+    <!-- Option to let applications specify that user data should
+         never be encrypted if an Encrypted File System solution
+         is enabled. Specifically, this is an "opt-out" feature, meaning
+         that, by default, user data will be encrypted if the EFS feature
+         is enabled. -->
+    <attr name="neverEncrypt" format="boolean" />
+
     <!-- Option to indicate this application is only for testing purposes.
          For example, it may expose functionality or data outside of itself
          that would cause a security hole, but is useful for testing.  This
@@ -715,6 +722,7 @@
         <attr name="killAfterRestore" />
         <attr name="restoreNeedsApplication" />
         <attr name="restoreAnyVersion" />
+        <attr name="neverEncrypt" />
     </declare-styleable>
     
     <!-- The <code>permission</code> tag declares a security permission that can be
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 0722fda..8a197e2 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -1298,7 +1298,7 @@
         format = PIXEL_FORMAT_RGBA_8888;
         break;
     case PIXEL_FORMAT_OPAQUE:
-        format = PIXEL_FORMAT_RGB_565;
+        format = PIXEL_FORMAT_RGBX_8888;
         break;
     }
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 9e4a16b..28bc599 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -117,6 +117,37 @@
      */
     public static final String KEY_LOCATION_CHANGED = "location";
 
+    /**
+     * Broadcast intent action indicating that the GPS has either been
+     * enabled or disabled. An intent extra provides this state as a boolean,
+     * where {@code true} means enabled.
+     * @see #EXTRA_GPS_ENABLED
+     *
+     * {@hide}
+     */
+    public static final String GPS_ENABLED_CHANGE_ACTION =
+        "android.location.GPS_ENABLED_CHANGE";
+
+    /**
+     * Broadcast intent action indicating that the GPS has either started or
+     * stopped receiving GPS fixes. An intent extra provides this state as a
+     * boolean, where {@code true} means that the GPS is actively receiving fixes.
+     * @see #EXTRA_GPS_ENABLED
+     *
+     * {@hide}
+     */
+    public static final String GPS_FIX_CHANGE_ACTION =
+        "android.location.GPS_FIX_CHANGE";
+
+    /**
+     * The lookup key for a boolean that indicates whether GPS is enabled or
+     * disabled. {@code true} means GPS is enabled. Retrieve it with
+     * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_GPS_ENABLED = "enabled";
+
     // Map from LocationListeners to their associated ListenerTransport objects
     private HashMap<LocationListener,ListenerTransport> mListeners =
         new HashMap<LocationListener,ListenerTransport>();
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index a5466d1..d3a71b3 100755
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -23,6 +23,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.location.LocationManager;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.util.Log;
@@ -81,7 +82,7 @@
     private final Context mContext;
     
     // parent gps location provider
-    private final GpsLocationProvider mGpsLocationProvider;
+    private final LocationManager mLocationManager;
     
     // configuration of notificaiton behavior
     private boolean mPlaySounds = false;
@@ -93,25 +94,25 @@
         
     public static class GpsNiNotification
     {
-    	int notificationId;
-    	int niType;
-    	boolean needNotify;
-    	boolean needVerify;
-    	boolean privacyOverride;
-    	int timeout;
-    	int defaultResponse;
-    	String requestorId;
-    	String text;
-    	int requestorIdEncoding;
-    	int textEncoding;
-    	Bundle extras;
+        public int notificationId;
+        public int niType;
+        public boolean needNotify;
+        public boolean needVerify;
+        public boolean privacyOverride;
+        public int timeout;
+        public int defaultResponse;
+        public String requestorId;
+        public String text;
+        public int requestorIdEncoding;
+        public int textEncoding;
+        public Bundle extras;
     };
     
     public static class GpsNiResponse {
-    	/* User reponse, one of the values in GpsUserResponseType */
-    	int userResponse;
-    	/* Optional extra data to pass with the user response */
-    	Bundle extras;
+        /* User reponse, one of the values in GpsUserResponseType */
+        int userResponse;
+        /* Optional extra data to pass with the user response */
+        Bundle extras;
     };
     
     /**
@@ -122,63 +123,57 @@
      */
     private Notification mNiNotification;
     
-    public GpsNetInitiatedHandler(Context context, GpsLocationProvider gpsLocationProvider) {
-    	mContext = context;       
-    	mGpsLocationProvider = gpsLocationProvider;
+    public GpsNetInitiatedHandler(Context context) {
+        mContext = context;
+        mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
     }
     
     // Handles NI events from HAL
     public void handleNiNotification(GpsNiNotification notif)
     {
-    	if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId 
-    			+ " requestorId: " + notif.requestorId + " text: " + notif.text);
-    	
-    	// Notify and verify with immediate pop-up
-    	if (notif.needNotify && notif.needVerify && mPopupImmediately)
-    	{
-    		// Popup the dialog box now
-    		openNiDialog(notif);
-    	}
-    	
-    	// Notify only, or delayed pop-up (change mPopupImmediately to FALSE) 
-    	if (notif.needNotify && !notif.needVerify ||
-    		notif.needNotify && notif.needVerify && !mPopupImmediately) 
-    	{
-    		// Show the notification
+        if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId
+                + " requestorId: " + notif.requestorId + " text: " + notif.text);
 
-    		// if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
-    		// when the user opens the notification message
-    		
-    		setNiNotification(notif);
-    	}
-    	
-    	// ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
-    	if ( notif.needNotify && !notif.needVerify || 
-    		!notif.needNotify && !notif.needVerify || 
-    		 notif.privacyOverride)
-    	{
-    		try {
-    			mGpsLocationProvider.getNetInitiatedListener().sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
-    		} 
-    		catch (RemoteException e)
-    		{
-    			Log.e(TAG, e.getMessage());
-    		}
-    	}
-    	
-    	//////////////////////////////////////////////////////////////////////////
-    	//   A note about timeout
-    	//   According to the protocol, in the need_notify and need_verify case,
-    	//   a default response should be sent when time out.
-    	//   
-    	//   In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
-    	//   and this class GpsNetInitiatedHandler does not need to do anything.
-    	//   
-    	//   However, the UI should at least close the dialog when timeout. Further, 
-    	//   for more general handling, timeout response should be added to the Handler here.
-    	//    	    	
+        // Notify and verify with immediate pop-up
+        if (notif.needNotify && notif.needVerify && mPopupImmediately)
+        {
+            // Popup the dialog box now
+            openNiDialog(notif);
+        }
+
+        // Notify only, or delayed pop-up (change mPopupImmediately to FALSE)
+        if (notif.needNotify && !notif.needVerify ||
+            notif.needNotify && notif.needVerify && !mPopupImmediately)
+        {
+            // Show the notification
+
+            // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
+            // when the user opens the notification message
+
+            setNiNotification(notif);
+        }
+
+        // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
+        if ( notif.needNotify && !notif.needVerify ||
+            !notif.needNotify && !notif.needVerify ||
+             notif.privacyOverride)
+        {
+            mLocationManager.sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
+        }
+
+        //////////////////////////////////////////////////////////////////////////
+        //   A note about timeout
+        //   According to the protocol, in the need_notify and need_verify case,
+        //   a default response should be sent when time out.
+        //
+        //   In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
+        //   and this class GpsNetInitiatedHandler does not need to do anything.
+        //
+        //   However, the UI should at least close the dialog when timeout. Further,
+        //   for more general handling, timeout response should be added to the Handler here.
+        //
     }
-    
+
     // Sets the NI notification.
     private synchronized void setNiNotification(GpsNiNotification notif) {
         NotificationManager notificationManager = (NotificationManager) mContext
@@ -186,272 +181,272 @@
         if (notificationManager == null) {
             return;
         }
-      
-    	String title = getNotifTitle(notif);
-    	String message = getNotifMessage(notif);
-        
+
+        String title = getNotifTitle(notif);
+        String message = getNotifMessage(notif);
+
         if (DEBUG) Log.d(TAG, "setNiNotification, notifyId: " + notif.notificationId +
-        		", title: " + title +
-        		", message: " + message);
-        
-    	// Construct Notification
-    	if (mNiNotification == null) {
-        	mNiNotification = new Notification();
-        	mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */
-        	mNiNotification.when = 0;
+                ", title: " + title +
+                ", message: " + message);
+
+        // Construct Notification
+        if (mNiNotification == null) {
+            mNiNotification = new Notification();
+            mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */
+            mNiNotification.when = 0;
         }
-    	
+
         if (mPlaySounds) {
-        	mNiNotification.defaults |= Notification.DEFAULT_SOUND;
+            mNiNotification.defaults |= Notification.DEFAULT_SOUND;
         } else {
-        	mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
+            mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
         }        
-        
+
         mNiNotification.flags = Notification.FLAG_ONGOING_EVENT;
         mNiNotification.tickerText = getNotifTicker(notif);
-        
+
         // if not to popup dialog immediately, pending intent will open the dialog
-        Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();    	        
+        Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
         PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);                
         mNiNotification.setLatestEventInfo(mContext, title, message, pi);
-        
+
         if (visible) {
             notificationManager.notify(notif.notificationId, mNiNotification);
         } else {
             notificationManager.cancel(notif.notificationId);
         }
     }
-    
-    // Opens the notification dialog and waits for user input
-    private void openNiDialog(GpsNiNotification notif) 
-    {
-    	Intent intent = getDlgIntent(notif);
-    	
-    	if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId + 
-    			", requestorId: " + notif.requestorId + 
-    			", text: " + notif.text);               	
 
-    	mContext.startActivity(intent);
+    // Opens the notification dialog and waits for user input
+    private void openNiDialog(GpsNiNotification notif)
+    {
+        Intent intent = getDlgIntent(notif);
+
+        if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId +
+                ", requestorId: " + notif.requestorId +
+                ", text: " + notif.text);
+
+        mContext.startActivity(intent);
     }
-    
+
     // Construct the intent for bringing up the dialog activity, which shows the 
     // notification and takes user input
     private Intent getDlgIntent(GpsNiNotification notif)
     {
-    	Intent intent = new Intent();
-    	String title = getDialogTitle(notif);
-    	String message = getDialogMessage(notif);
-    	
-    	// directly bring up the NI activity
-    	intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-    	intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);    	
+        Intent intent = new Intent();
+        String title = getDialogTitle(notif);
+        String message = getDialogMessage(notif);
 
-    	// put data in the intent
-    	intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);    	
-    	intent.putExtra(NI_INTENT_KEY_TITLE, title);
-    	intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
-    	intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
-    	intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
-    	
-    	if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
-    			", timeout: " + notif.timeout);
-    	
-    	return intent;
+        // directly bring up the NI activity
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
+
+        // put data in the intent
+        intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);
+        intent.putExtra(NI_INTENT_KEY_TITLE, title);
+        intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
+        intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
+        intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
+
+        if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
+                ", timeout: " + notif.timeout);
+
+        return intent;
     }
-    
+
     // Converts a string (or Hex string) to a char array
     static byte[] stringToByteArray(String original, boolean isHex)
     {
-    	int length = isHex ? original.length() / 2 : original.length();
-    	byte[] output = new byte[length];
-    	int i;
-    	
-    	if (isHex)
-    	{
-    		for (i = 0; i < length; i++)
-    		{
-    			output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
-    		}
-    	}
-    	else {
-    		for (i = 0; i < length; i++)
-    		{
-    			output[i] = (byte) original.charAt(i);
-    		}
-    	}
-    	
-    	return output;
+        int length = isHex ? original.length() / 2 : original.length();
+        byte[] output = new byte[length];
+        int i;
+
+        if (isHex)
+        {
+            for (i = 0; i < length; i++)
+            {
+                output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
+            }
+        }
+        else {
+            for (i = 0; i < length; i++)
+            {
+                output[i] = (byte) original.charAt(i);
+            }
+        }
+
+        return output;
     }
-    
+
     /**
      * Unpacks an byte array containing 7-bit packed characters into a String.
-     * 
+     *
      * @param input a 7-bit packed char array
      * @return the unpacked String
      */
     static String decodeGSMPackedString(byte[] input)
     {
-    	final char CHAR_CR = 0x0D;
-    	int nStridx = 0;
-    	int nPckidx = 0;
-    	int num_bytes = input.length;
-    	int cPrev = 0;
-    	int cCurr = 0;
-    	byte nShift;
-    	byte nextChar;
-    	byte[] stringBuf = new byte[input.length * 2]; 
-    	String result = "";
-    	
-    	while(nPckidx < num_bytes)
-    	{
-    		nShift = (byte) (nStridx & 0x07);
-    		cCurr = input[nPckidx++];
-    		if (cCurr < 0) cCurr += 256;
+        final char CHAR_CR = 0x0D;
+        int nStridx = 0;
+        int nPckidx = 0;
+        int num_bytes = input.length;
+        int cPrev = 0;
+        int cCurr = 0;
+        byte nShift;
+        byte nextChar;
+        byte[] stringBuf = new byte[input.length * 2];
+        String result = "";
 
-    		/* A 7-bit character can be split at the most between two bytes of packed
-    		 ** data.
-    		 */
-    		nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
-    		stringBuf[nStridx++] = nextChar;
+        while(nPckidx < num_bytes)
+        {
+            nShift = (byte) (nStridx & 0x07);
+            cCurr = input[nPckidx++];
+            if (cCurr < 0) cCurr += 256;
 
-    		/* Special case where the whole of the next 7-bit character fits inside
-    		 ** the current byte of packed data.
-    		 */
-    		if(nShift == 6)
-    		{
-    			/* If the next 7-bit character is a CR (0x0D) and it is the last
-    			 ** character, then it indicates a padding character. Drop it.
-    			 */
-    			if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
-    			{
-    				break;
-    			}
-    			
-    			nextChar = (byte) (cCurr >> 1); 
-    			stringBuf[nStridx++] = nextChar;
-    		}
+            /* A 7-bit character can be split at the most between two bytes of packed
+             ** data.
+             */
+            nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
+            stringBuf[nStridx++] = nextChar;
 
-    		cPrev = cCurr;
-    	}
-    	
-    	try{
-    		result = new String(stringBuf, 0, nStridx, "US-ASCII");
-    	}
-    	catch (UnsupportedEncodingException e)
-    	{
-    		Log.e(TAG, e.getMessage());
-    	}
-    	
-    	return result;
+            /* Special case where the whole of the next 7-bit character fits inside
+             ** the current byte of packed data.
+             */
+            if(nShift == 6)
+            {
+                /* If the next 7-bit character is a CR (0x0D) and it is the last
+                 ** character, then it indicates a padding character. Drop it.
+                 */
+                if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
+                {
+                    break;
+                }
+
+                nextChar = (byte) (cCurr >> 1);
+                stringBuf[nStridx++] = nextChar;
+            }
+
+            cPrev = cCurr;
+        }
+
+        try {
+            result = new String(stringBuf, 0, nStridx, "US-ASCII");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            Log.e(TAG, e.getMessage());
+        }
+
+        return result;
     }
-    
+
     static String decodeUTF8String(byte[] input)
     {
-    	String decoded = "";
-    	try {
-    		decoded = new String(input, "UTF-8");
-    	}
-    	catch (UnsupportedEncodingException e)
-    	{ 
-    		Log.e(TAG, e.getMessage());
-    	} 
-		return decoded;
+        String decoded = "";
+        try {
+            decoded = new String(input, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            Log.e(TAG, e.getMessage());
+        }
+        return decoded;
     }
-    
+
     static String decodeUCS2String(byte[] input)
     {
-    	String decoded = "";
-    	try {
-    		decoded = new String(input, "UTF-16");
-    	}
-    	catch (UnsupportedEncodingException e)
-    	{ 
-    		Log.e(TAG, e.getMessage());
-    	} 
-		return decoded;
+        String decoded = "";
+        try {
+            decoded = new String(input, "UTF-16");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            Log.e(TAG, e.getMessage());
+        }
+        return decoded;
     }
-    
+
     /** Decode NI string
-     * 
+     *
      * @param original   The text string to be decoded
      * @param isHex      Specifies whether the content of the string has been encoded as a Hex string. Encoding
-     *                   a string as Hex can allow zeros inside the coded text. 
+     *                   a string as Hex can allow zeros inside the coded text.
      * @param coding     Specifies the coding scheme of the string, such as GSM, UTF8, UCS2, etc. This coding scheme
-     * 					 needs to match those used passed to HAL from the native GPS driver. Decoding is done according
+     *                      needs to match those used passed to HAL from the native GPS driver. Decoding is done according
      *                   to the <code> coding </code>, after a Hex string is decoded. Generally, if the
-     *                   notification strings don't need further decoding, <code> coding </code> encoding can be 
+     *                   notification strings don't need further decoding, <code> coding </code> encoding can be
      *                   set to -1, and <code> isHex </code> can be false.
      * @return the decoded string
      */
     static private String decodeString(String original, boolean isHex, int coding)
     {
-    	String decoded = original;
-    	byte[] input = stringToByteArray(original, isHex);
+        String decoded = original;
+        byte[] input = stringToByteArray(original, isHex);
 
-    	switch (coding) {
-    	case GPS_ENC_NONE:
-    		decoded = original;
-    		break;
-    		
-    	case GPS_ENC_SUPL_GSM_DEFAULT:
-    		decoded = decodeGSMPackedString(input);
-    		break;
-    		
-    	case GPS_ENC_SUPL_UTF8:
-    		decoded = decodeUTF8String(input);
-    		break;
-    		
-    	case GPS_ENC_SUPL_UCS2:
-    		decoded = decodeUCS2String(input);
-    		break;
-    		
-    	case GPS_ENC_UNKNOWN:
-    		decoded = original;
-    		break;
-    		
-    	default:
-    		Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
-    		break;
-    	}
-    	return decoded;
+        switch (coding) {
+        case GPS_ENC_NONE:
+            decoded = original;
+            break;
+
+        case GPS_ENC_SUPL_GSM_DEFAULT:
+            decoded = decodeGSMPackedString(input);
+            break;
+
+        case GPS_ENC_SUPL_UTF8:
+            decoded = decodeUTF8String(input);
+            break;
+
+        case GPS_ENC_SUPL_UCS2:
+            decoded = decodeUCS2String(input);
+            break;
+
+        case GPS_ENC_UNKNOWN:
+            decoded = original;
+            break;
+
+        default:
+            Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
+            break;
+        }
+        return decoded;
     }
-    
+
     // change this to configure notification display
     static private String getNotifTicker(GpsNiNotification notif)
     {
-    	String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
-    			decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
-    			decodeString(notif.text, mIsHexInput, notif.textEncoding));
-    	return ticker;
+        String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
+                decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+                decodeString(notif.text, mIsHexInput, notif.textEncoding));
+        return ticker;
     }
-    
+
     // change this to configure notification display
     static private String getNotifTitle(GpsNiNotification notif)
     {
-    	String title = String.format("Position Request");
-    	return title;
+        String title = String.format("Position Request");
+        return title;
     }
-    
+
     // change this to configure notification display
     static private String getNotifMessage(GpsNiNotification notif)
     {
-    	String message = String.format(
-    			"NI Request received from [%s] for client [%s]!", 
-    			decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
-    			decodeString(notif.text, mIsHexInput, notif.textEncoding));
-    	return message;
+        String message = String.format(
+                "NI Request received from [%s] for client [%s]!",
+                decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+                decodeString(notif.text, mIsHexInput, notif.textEncoding));
+        return message;
     }       
-    
+
     // change this to configure dialog display (for verification)
     static public String getDialogTitle(GpsNiNotification notif)
     {
-    	return getNotifTitle(notif);
+        return getNotifTitle(notif);
     }
-    
+
     // change this to configure dialog display (for verification)
     static private String getDialogMessage(GpsNiNotification notif)
     {
-    	return getNotifMessage(notif);
+        return getNotifMessage(notif);
     }
-    
+
 }
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index d057ab7..dd0d064 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -8,6 +8,8 @@
                  android:backupAgent="SettingsBackupAgent"
                  android:killAfterRestore="false"
                  android:icon="@drawable/ic_launcher_settings">
+                 
+    <!-- todo add: android:neverEncrypt="true" -->
 
         <provider android:name="SettingsProvider" android:authorities="settings"
                   android:multiprocess="false"
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index 5672a01..eeafd5a 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -52,8 +52,11 @@
     // The actual implementation is delegated to the VpnService class.
     private VpnService<? extends VpnProfile> mService;
 
+    // TODO(oam): Test VPN when EFS is enabled (will do later)...
     private static String getStateFilePath() {
-	return Environment.getDataDirectory().getPath() + STATES_FILE_RELATIVE_PATH;
+        // This call will return the correcu directory whether Encrypted FS is enabled or not
+        // Disabled: /data/misc/vpn/.states   Enabled: /data/secure/misc/vpn/.states
+	return Environment.getSecureDataDirectory().getPath() + STATES_FILE_RELATIVE_PATH;
     }
 
     private final IBinder mBinder = new IVpnService.Stub() {
diff --git a/policy/Android.mk b/policy/Android.mk
new file mode 100644
index 0000000..a887142
--- /dev/null
+++ b/policy/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+
+# the library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+            $(call all-subdir-java-files)
+            
+LOCAL_MODULE := android.policy
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/policy/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/com/android/internal/policy/impl/AccountUnlockScreen.java
new file mode 100644
index 0000000..9921069
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/AccountUnlockScreen.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import com.android.internal.R;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.OperationCanceledException;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.AccountManagerCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.telephony.TelephonyManager;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.LoginFilter;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.os.Bundle;
+
+import java.io.IOException;
+
+/**
+ * When the user forgets their password a bunch of times, we fall back on their
+ * account's login/password to unlock the phone (and reset their lock pattern).
+ */
+public class AccountUnlockScreen extends RelativeLayout implements KeyguardScreen,
+        KeyguardUpdateMonitor.InfoCallback,View.OnClickListener, TextWatcher {
+    private static final String LOCK_PATTERN_PACKAGE = "com.android.settings";
+    private static final String LOCK_PATTERN_CLASS =
+            "com.android.settings.ChooseLockPattern";
+
+    /**
+     * The amount of millis to stay awake once this screen detects activity
+     */
+    private static final int AWAKE_POKE_MILLIS = 30000;
+
+    private final KeyguardScreenCallback mCallback;
+    private final LockPatternUtils mLockPatternUtils;
+
+    private TextView mTopHeader;
+    private TextView mInstructions;
+    private EditText mLogin;
+    private EditText mPassword;
+    private Button mOk;
+    private Button mEmergencyCall;
+
+    /**
+     * Shown while making asynchronous check of password.
+     */
+    private ProgressDialog mCheckingDialog;
+
+    /**
+     * AccountUnlockScreen constructor.
+     * @param configuration
+     */
+    public AccountUnlockScreen(Context context,Configuration configuration,
+            KeyguardScreenCallback callback, LockPatternUtils lockPatternUtils) {
+        super(context);
+        mCallback = callback;
+        mLockPatternUtils = lockPatternUtils;
+
+        LayoutInflater.from(context).inflate(
+                R.layout.keyguard_screen_glogin_unlock, this, true);
+
+        mTopHeader = (TextView) findViewById(R.id.topHeader);
+        mTopHeader.setText(mLockPatternUtils.isPermanentlyLocked() ?
+                R.string.lockscreen_glogin_too_many_attempts :
+                R.string.lockscreen_glogin_forgot_pattern);
+
+        mInstructions = (TextView) findViewById(R.id.instructions);
+
+        mLogin = (EditText) findViewById(R.id.login);
+        mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } );
+        mLogin.addTextChangedListener(this);
+
+        mPassword = (EditText) findViewById(R.id.password);
+        mPassword.addTextChangedListener(this);
+
+        mOk = (Button) findViewById(R.id.ok);
+        mOk.setOnClickListener(this);
+
+        mEmergencyCall = (Button) findViewById(R.id.emergencyCall);
+        mEmergencyCall.setOnClickListener(this);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall);
+    }
+
+    public void afterTextChanged(Editable s) {
+    }
+
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction,
+            Rect previouslyFocusedRect) {
+        // send focus to the login field
+        return mLogin.requestFocus(direction, previouslyFocusedRect);
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        // start fresh
+        mLogin.setText("");
+        mPassword.setText("");
+        mLogin.requestFocus();
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall);
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        if (mCheckingDialog != null) {
+            mCheckingDialog.hide();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void onClick(View v) {
+        mCallback.pokeWakelock();
+        if (v == mOk) {
+            asyncCheckPassword();
+        }
+
+        if (v == mEmergencyCall) {
+            mCallback.takeEmergencyCallAction();
+        }
+    }
+
+    private void onCheckPasswordResult(boolean success) {
+        if (success) {
+            // clear out forgotten password
+            mLockPatternUtils.setPermanentlyLocked(false);
+            mLockPatternUtils.setLockPatternEnabled(false);
+            mLockPatternUtils.saveLockPattern(null);
+
+            // launch the 'choose lock pattern' activity so
+            // the user can pick a new one if they want to
+            Intent intent = new Intent();
+            intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(intent);
+            mCallback.reportSuccessfulUnlockAttempt();
+
+            // close the keyguard
+            mCallback.keyguardDone(true);
+        } else {
+            mInstructions.setText(R.string.lockscreen_glogin_invalid_input);
+            mPassword.setText("");
+            mCallback.reportFailedUnlockAttempt();
+        }
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN
+                && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+            if (mLockPatternUtils.isPermanentlyLocked()) {
+                mCallback.goToLockScreen();
+            } else {
+                mCallback.forgotPattern(false);
+            }
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    /**
+     * Given the string the user entered in the 'username' field, find
+     * the stored account that they probably intended.  Prefer, in order:
+     *
+     *   - an exact match for what was typed, or
+     *   - a case-insensitive match for what was typed, or
+     *   - if they didn't include a domain, an exact match of the username, or
+     *   - if they didn't include a domain, a case-insensitive
+     *     match of the username.
+     *
+     * If there is a tie for the best match, choose neither --
+     * the user needs to be more specific.
+     *
+     * @return an account name from the database, or null if we can't
+     * find a single best match.
+     */
+    private Account findIntendedAccount(String username) {
+        Account[] accounts = AccountManager.get(mContext).getAccountsByType("com.google");
+
+        // Try to figure out which account they meant if they
+        // typed only the username (and not the domain), or got
+        // the case wrong.
+
+        Account bestAccount = null;
+        int bestScore = 0;
+        for (Account a: accounts) {
+            int score = 0;
+            if (username.equals(a.name)) {
+                score = 4;
+            } else if (username.equalsIgnoreCase(a.name)) {
+                score = 3;
+            } else if (username.indexOf('@') < 0) {
+                int i = a.name.indexOf('@');
+                if (i >= 0) {
+                    String aUsername = a.name.substring(0, i);
+                    if (username.equals(aUsername)) {
+                        score = 2;
+                    } else if (username.equalsIgnoreCase(aUsername)) {
+                        score = 1;
+                    }
+                }
+            }
+            if (score > bestScore) {
+                bestAccount = a;
+                bestScore = score;
+            } else if (score == bestScore) {
+                bestAccount = null;
+            }
+        }
+        return bestAccount;
+    }
+
+    private void asyncCheckPassword() {
+        mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
+        final String login = mLogin.getText().toString();
+        final String password = mPassword.getText().toString();
+        Account account = findIntendedAccount(login);
+        if (account == null) {
+            onCheckPasswordResult(false);
+            return;
+        }
+        getProgressDialog().show();
+        Bundle options = new Bundle();
+        options.putString(AccountManager.KEY_PASSWORD, password);
+        AccountManager.get(mContext).confirmCredentials(account, options, null /* activity */,
+                new AccountManagerCallback<Bundle>() {
+            public void run(AccountManagerFuture<Bundle> future) {
+                try {
+                    mCallback.pokeWakelock(AWAKE_POKE_MILLIS);
+                    final Bundle result = future.getResult();
+                    final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
+                    // ensure on UI thread
+                    mLogin.post(new Runnable() {
+                        public void run() {
+                            onCheckPasswordResult(verified);
+                        }
+                    });
+                } catch (OperationCanceledException e) {
+                    onCheckPasswordResult(false);
+                } catch (IOException e) {
+                    onCheckPasswordResult(false);
+                } catch (AuthenticatorException e) {
+                    onCheckPasswordResult(false);
+                } finally {
+                    mLogin.post(new Runnable() {
+                        public void run() {
+                            getProgressDialog().hide();
+                        }
+                    });
+                }
+            }
+        }, null /* handler */);
+    }
+
+    private Dialog getProgressDialog() {
+        if (mCheckingDialog == null) {
+            mCheckingDialog = new ProgressDialog(mContext);
+            mCheckingDialog.setMessage(
+                    mContext.getString(R.string.lockscreen_glogin_checking_password));
+            mCheckingDialog.setIndeterminate(true);
+            mCheckingDialog.setCancelable(false);
+            mCheckingDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_sf_slowBlur)) {
+                mCheckingDialog.getWindow().setFlags(
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+            }
+        }
+        return mCheckingDialog;
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCall);
+    }
+
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+
+    }
+
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+
+    }
+
+    public void onRingerModeChanged(int state) {
+
+    }
+
+    public void onTimeChanged() {
+
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/GlobalActions.java b/policy/com/android/internal/policy/impl/GlobalActions.java
new file mode 100644
index 0000000..1f06dcc
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/GlobalActions.java
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Helper to show the global actions dialog.  Each item is an {@link Action} that
+ * may show depending on whether the keyguard is showing, and whether the device
+ * is provisioned.
+ */
+class GlobalActions implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener  {
+
+    private static final String TAG = "GlobalActions";
+
+    private StatusBarManager mStatusBar;
+
+    private final Context mContext;
+    private final AudioManager mAudioManager;
+
+    private ArrayList<Action> mItems;
+    private AlertDialog mDialog;
+
+    private ToggleAction mSilentModeToggle;
+    private ToggleAction mAirplaneModeOn;
+
+    private MyAdapter mAdapter;
+
+    private boolean mKeyguardShowing = false;
+    private boolean mDeviceProvisioned = false;
+    private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
+    private boolean mIsWaitingForEcmExit = false;
+
+    /**
+     * @param context everything needs a context :(
+     */
+    public GlobalActions(Context context) {
+        mContext = context;
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        // receive broadcasts
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+        context.registerReceiver(mBroadcastReceiver, filter);
+
+        // get notified of phone state changes
+        TelephonyManager telephonyManager =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+    }
+
+    /**
+     * Show the global actions dialog (creating if necessary)
+     * @param keyguardShowing True if keyguard is showing
+     */
+    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
+        mKeyguardShowing = keyguardShowing;
+        mDeviceProvisioned = isDeviceProvisioned;
+        if (mDialog == null) {
+            mStatusBar = (StatusBarManager)mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+            mDialog = createDialog();
+        }
+        prepareDialog();
+
+        mStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
+        mDialog.show();
+    }
+
+    /**
+     * Create the global actions dialog.
+     * @return A new dialog.
+     */
+    private AlertDialog createDialog() {
+        mSilentModeToggle = new ToggleAction(
+                R.drawable.ic_lock_silent_mode,
+                R.drawable.ic_lock_silent_mode_off,
+                R.string.global_action_toggle_silent_mode,
+                R.string.global_action_silent_mode_on_status,
+                R.string.global_action_silent_mode_off_status) {
+
+            void willCreate() {
+                // XXX: FIXME: switch to ic_lock_vibrate_mode when available
+                mEnabledIconResId = (Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.VIBRATE_IN_SILENT, 1) == 1)
+                    ? R.drawable.ic_lock_silent_mode_vibrate
+                    : R.drawable.ic_lock_silent_mode;
+            }
+
+            void onToggle(boolean on) {
+                if (on) {
+                    mAudioManager.setRingerMode((Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.VIBRATE_IN_SILENT, 1) == 1)
+                        ? AudioManager.RINGER_MODE_VIBRATE
+                        : AudioManager.RINGER_MODE_SILENT);
+                } else {
+                    mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+                }
+            }
+
+            public boolean showDuringKeyguard() {
+                return true;
+            }
+
+            public boolean showBeforeProvisioning() {
+                return false;
+            }
+        };
+
+        mAirplaneModeOn = new ToggleAction(
+                R.drawable.ic_lock_airplane_mode,
+                R.drawable.ic_lock_airplane_mode_off,
+                R.string.global_actions_toggle_airplane_mode,
+                R.string.global_actions_airplane_mode_on_status,
+                R.string.global_actions_airplane_mode_off_status) {
+
+            void onToggle(boolean on) {
+                if (Boolean.parseBoolean(
+                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
+                    mIsWaitingForEcmExit = true;
+                    // Launch ECM exit dialog
+                    Intent ecmDialogIntent =
+                            new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
+                    ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    mContext.startActivity(ecmDialogIntent);
+                } else {
+                    changeAirplaneModeSystemSetting(on);
+                }
+            }
+
+            @Override
+            protected void changeStateFromPress(boolean buttonOn) {
+                // In ECM mode airplane state cannot be changed
+                if (!(Boolean.parseBoolean(
+                        SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)))) {
+                    mState = buttonOn ? State.TurningOn : State.TurningOff;
+                    mAirplaneState = mState;
+                }
+            }
+
+            public boolean showDuringKeyguard() {
+                return true;
+            }
+
+            public boolean showBeforeProvisioning() {
+                return false;
+            }
+        };
+
+        mItems = Lists.newArrayList(
+                // silent mode
+                mSilentModeToggle,
+                // next: airplane mode
+                mAirplaneModeOn,
+                // last: power off
+                new SinglePressAction(
+                        com.android.internal.R.drawable.ic_lock_power_off,
+                        R.string.global_action_power_off) {
+
+                    public void onPress() {
+                        // shutdown by making sure radio and power are handled accordingly.
+                        ShutdownThread.shutdown(mContext, true);
+                    }
+
+                    public boolean showDuringKeyguard() {
+                        return true;
+                    }
+
+                    public boolean showBeforeProvisioning() {
+                        return true;
+                    }
+                });
+
+        mAdapter = new MyAdapter();
+
+        final AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
+
+        ab.setAdapter(mAdapter, this)
+                .setInverseBackgroundForced(true)
+                .setTitle(R.string.global_actions);
+
+        final AlertDialog dialog = ab.create();
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        if (!mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_slowBlur)) {
+            dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+        }
+
+        dialog.setOnDismissListener(this);
+
+        return dialog;
+    }
+
+    private void prepareDialog() {
+        final boolean silentModeOn =
+                mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+        mSilentModeToggle.updateState(
+                silentModeOn ? ToggleAction.State.On : ToggleAction.State.Off);
+        mAirplaneModeOn.updateState(mAirplaneState);
+        mAdapter.notifyDataSetChanged();
+        if (mKeyguardShowing) {
+            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        } else {
+            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        }
+    }
+
+
+    /** {@inheritDoc} */
+    public void onDismiss(DialogInterface dialog) {
+        mStatusBar.disable(StatusBarManager.DISABLE_NONE);
+    }
+
+    /** {@inheritDoc} */
+    public void onClick(DialogInterface dialog, int which) {
+        dialog.dismiss();
+        mAdapter.getItem(which).onPress();
+    }
+
+
+    /**
+     * The adapter used for the list within the global actions dialog, taking
+     * into account whether the keyguard is showing via
+     * {@link GlobalActions#mKeyguardShowing} and whether the device is provisioned
+     * via {@link GlobalActions#mDeviceProvisioned}.
+     */
+    private class MyAdapter extends BaseAdapter {
+
+        public int getCount() {
+            int count = 0;
+
+            for (int i = 0; i < mItems.size(); i++) {
+                final Action action = mItems.get(i);
+
+                if (mKeyguardShowing && !action.showDuringKeyguard()) {
+                    continue;
+                }
+                if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
+                    continue;
+                }
+                count++;
+            }
+            return count;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return getItem(position).isEnabled();
+        }
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+
+        public Action getItem(int position) {
+
+            int filteredPos = 0;
+            for (int i = 0; i < mItems.size(); i++) {
+                final Action action = mItems.get(i);
+                if (mKeyguardShowing && !action.showDuringKeyguard()) {
+                    continue;
+                }
+                if (!mDeviceProvisioned && !action.showBeforeProvisioning()) {
+                    continue;
+                }
+                if (filteredPos == position) {
+                    return action;
+                }
+                filteredPos++;
+            }
+
+            throw new IllegalArgumentException("position " + position + " out of "
+                    + "range of showable actions, filtered count = "
+                    + "= " + getCount() + ", keyguardshowing=" + mKeyguardShowing
+                    + ", provisioned=" + mDeviceProvisioned);
+        }
+
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            Action action = getItem(position);
+            return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
+        }
+    }
+
+    // note: the scheme below made more sense when we were planning on having
+    // 8 different things in the global actions dialog.  seems overkill with
+    // only 3 items now, but may as well keep this flexible approach so it will
+    // be easy should someone decide at the last minute to include something
+    // else, such as 'enable wifi', or 'enable bluetooth'
+
+    /**
+     * What each item in the global actions dialog must be able to support.
+     */
+    private interface Action {
+        View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater);
+
+        void onPress();
+
+        /**
+         * @return whether this action should appear in the dialog when the keygaurd
+         *    is showing.
+         */
+        boolean showDuringKeyguard();
+
+        /**
+         * @return whether this action should appear in the dialog before the
+         *   device is provisioned.
+         */
+        boolean showBeforeProvisioning();
+
+        boolean isEnabled();
+    }
+
+    /**
+     * A single press action maintains no state, just responds to a press
+     * and takes an action.
+     */
+    private static abstract class SinglePressAction implements Action {
+        private final int mIconResId;
+        private final int mMessageResId;
+
+        protected SinglePressAction(int iconResId, int messageResId) {
+            mIconResId = iconResId;
+            mMessageResId = messageResId;
+        }
+
+        public boolean isEnabled() {
+            return true;
+        }
+
+        abstract public void onPress();
+
+        public View create(
+                Context context, View convertView, ViewGroup parent, LayoutInflater inflater) {
+            View v = (convertView != null) ?
+                    convertView :
+                    inflater.inflate(R.layout.global_actions_item, parent, false);
+
+            ImageView icon = (ImageView) v.findViewById(R.id.icon);
+            TextView messageView = (TextView) v.findViewById(R.id.message);
+
+            v.findViewById(R.id.status).setVisibility(View.GONE);
+
+            icon.setImageDrawable(context.getResources().getDrawable(mIconResId));
+            messageView.setText(mMessageResId);
+
+            return v;
+        }
+    }
+
+    /**
+     * A toggle action knows whether it is on or off, and displays an icon
+     * and status message accordingly.
+     */
+    private static abstract class ToggleAction implements Action {
+
+        enum State {
+            Off(false),
+            TurningOn(true),
+            TurningOff(true),
+            On(false);
+
+            private final boolean inTransition;
+
+            State(boolean intermediate) {
+                inTransition = intermediate;
+            }
+
+            public boolean inTransition() {
+                return inTransition;
+            }
+        }
+
+        protected State mState = State.Off;
+
+        // prefs
+        protected int mEnabledIconResId;
+        protected int mDisabledIconResid;
+        protected int mMessageResId;
+        protected int mEnabledStatusMessageResId;
+        protected int mDisabledStatusMessageResId;
+
+        /**
+         * @param enabledIconResId The icon for when this action is on.
+         * @param disabledIconResid The icon for when this action is off.
+         * @param essage The general information message, e.g 'Silent Mode'
+         * @param enabledStatusMessageResId The on status message, e.g 'sound disabled'
+         * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled'
+         */
+        public ToggleAction(int enabledIconResId,
+                int disabledIconResid,
+                int essage,
+                int enabledStatusMessageResId,
+                int disabledStatusMessageResId) {
+            mEnabledIconResId = enabledIconResId;
+            mDisabledIconResid = disabledIconResid;
+            mMessageResId = essage;
+            mEnabledStatusMessageResId = enabledStatusMessageResId;
+            mDisabledStatusMessageResId = disabledStatusMessageResId;
+        }
+
+        /**
+         * Override to make changes to resource IDs just before creating the
+         * View.
+         */
+        void willCreate() {
+
+        }
+
+        public View create(Context context, View convertView, ViewGroup parent,
+                LayoutInflater inflater) {
+            willCreate();
+
+            View v = (convertView != null) ?
+                    convertView :
+                    inflater.inflate(R
+                            .layout.global_actions_item, parent, false);
+
+            ImageView icon = (ImageView) v.findViewById(R.id.icon);
+            TextView messageView = (TextView) v.findViewById(R.id.message);
+            TextView statusView = (TextView) v.findViewById(R.id.status);
+
+            messageView.setText(mMessageResId);
+
+            boolean on = ((mState == State.On) || (mState == State.TurningOn));
+            icon.setImageDrawable(context.getResources().getDrawable(
+                    (on ? mEnabledIconResId : mDisabledIconResid)));
+            statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId);
+            statusView.setVisibility(View.VISIBLE);
+
+            final boolean enabled = isEnabled();
+            messageView.setEnabled(enabled);
+            statusView.setEnabled(enabled);
+            icon.setEnabled(enabled);
+            v.setEnabled(enabled);
+
+            return v;
+        }
+
+        public final void onPress() {
+            if (mState.inTransition()) {
+                Log.w(TAG, "shouldn't be able to toggle when in transition");
+                return;
+            }
+
+            final boolean nowOn = !(mState == State.On);
+            onToggle(nowOn);
+            changeStateFromPress(nowOn);
+        }
+
+        public boolean isEnabled() {
+            return !mState.inTransition();
+        }
+
+        /**
+         * Implementations may override this if their state can be in on of the intermediate
+         * states until some notification is received (e.g airplane mode is 'turning off' until
+         * we know the wireless connections are back online
+         * @param buttonOn Whether the button was turned on or off
+         */
+        protected void changeStateFromPress(boolean buttonOn) {
+            mState = buttonOn ? State.On : State.Off;
+        }
+
+        abstract void onToggle(boolean on);
+
+        public void updateState(State state) {
+            mState = state;
+        }
+    }
+
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
+                String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
+                if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS.equals(reason)) {
+                    mHandler.sendEmptyMessage(MESSAGE_DISMISS);
+                }
+            } else if (TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(action)) {
+                // Airplane mode can be changed after ECM exits if airplane toggle button
+                // is pressed during ECM mode
+                if (!(intent.getBooleanExtra("PHONE_IN_ECM_STATE", false)) &&
+                        mIsWaitingForEcmExit) {
+                    mIsWaitingForEcmExit = false;
+                    changeAirplaneModeSystemSetting(true);
+                }
+            }
+        }
+    };
+
+    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
+            mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
+            mAirplaneModeOn.updateState(mAirplaneState);
+            mAdapter.notifyDataSetChanged();
+        }
+    };
+
+    private static final int MESSAGE_DISMISS = 0;
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            if (msg.what == MESSAGE_DISMISS) {
+                if (mDialog != null) {
+                    mDialog.dismiss();
+                }
+            }
+        }
+    };
+
+    /**
+     * Change the airplane mode system setting
+     */
+    private void changeAirplaneModeSystemSetting(boolean on) {
+        Settings.System.putInt(
+                mContext.getContentResolver(),
+                Settings.System.AIRPLANE_MODE_ON,
+                on ? 1 : 0);
+        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.putExtra("state", on);
+        mContext.sendBroadcast(intent);
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/IconUtilities.java b/policy/com/android/internal/policy/impl/IconUtilities.java
new file mode 100644
index 0000000..99055cf
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/IconUtilities.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.TableMaskFilter;
+import android.graphics.Typeface;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.content.res.Resources;
+import android.content.Context;
+
+/**
+ * Various utilities shared amongst the Launcher's classes.
+ */
+final class IconUtilities {
+    private static final String TAG = "IconUtilities";
+
+    private static final int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
+
+    private int mIconWidth = -1;
+    private int mIconHeight = -1;
+    private int mIconTextureWidth = -1;
+    private int mIconTextureHeight = -1;
+
+    private final Paint mPaint = new Paint();
+    private final Paint mBlurPaint = new Paint();
+    private final Paint mGlowColorPressedPaint = new Paint();
+    private final Paint mGlowColorFocusedPaint = new Paint();
+    private final Rect mOldBounds = new Rect();
+    private final Canvas mCanvas = new Canvas();
+    private final DisplayMetrics mDisplayMetrics;
+
+    private int mColorIndex = 0;
+
+    public IconUtilities(Context context) {
+        final Resources resources = context.getResources();
+        DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
+        final float density = metrics.density;
+        final float blurPx = 5 * density;
+
+        mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
+        mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
+
+        mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL));
+        mGlowColorPressedPaint.setColor(0xffffc300);
+        mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
+        mGlowColorFocusedPaint.setColor(0xffff8e00);
+        mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
+
+        ColorMatrix cm = new ColorMatrix();
+        cm.setSaturation(0.2f);
+
+        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
+                Paint.FILTER_BITMAP_FLAG));
+    }
+
+    public Drawable createIconDrawable(Drawable src) {
+        Bitmap scaled = createIconBitmap(src);
+
+        StateListDrawable result = new StateListDrawable();
+
+        result.addState(new int[] { android.R.attr.state_focused },
+                new BitmapDrawable(createSelectedBitmap(scaled, false)));
+        result.addState(new int[] { android.R.attr.state_pressed },
+                new BitmapDrawable(createSelectedBitmap(scaled, true)));
+        result.addState(new int[0], new BitmapDrawable(scaled));
+
+        result.setBounds(0, 0, mIconTextureWidth, mIconTextureHeight);
+        return result;
+    }
+
+    /**
+     * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
+     * of two sized ARGB_8888 bitmap that can be used as a gl texture.
+     */
+    private Bitmap createIconBitmap(Drawable icon) {
+        int width = mIconWidth;
+        int height = mIconHeight;
+
+        if (icon instanceof PaintDrawable) {
+            PaintDrawable painter = (PaintDrawable) icon;
+            painter.setIntrinsicWidth(width);
+            painter.setIntrinsicHeight(height);
+        } else if (icon instanceof BitmapDrawable) {
+            // Ensure the bitmap has a density.
+            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
+            Bitmap bitmap = bitmapDrawable.getBitmap();
+            if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
+                bitmapDrawable.setTargetDensity(mDisplayMetrics);
+            }
+        }
+        int sourceWidth = icon.getIntrinsicWidth();
+        int sourceHeight = icon.getIntrinsicHeight();
+
+        if (sourceWidth > 0 && sourceWidth > 0) {
+            // There are intrinsic sizes.
+            if (width < sourceWidth || height < sourceHeight) {
+                // It's too big, scale it down.
+                final float ratio = (float) sourceWidth / sourceHeight;
+                if (sourceWidth > sourceHeight) {
+                    height = (int) (width / ratio);
+                } else if (sourceHeight > sourceWidth) {
+                    width = (int) (height * ratio);
+                }
+            } else if (sourceWidth < width && sourceHeight < height) {
+                // It's small, use the size they gave us.
+                width = sourceWidth;
+                height = sourceHeight;
+            }
+        }
+
+        // no intrinsic size --> use default size
+        int textureWidth = mIconTextureWidth;
+        int textureHeight = mIconTextureHeight;
+
+        final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
+                Bitmap.Config.ARGB_8888);
+        final Canvas canvas = mCanvas;
+        canvas.setBitmap(bitmap);
+
+        final int left = (textureWidth-width) / 2;
+        final int top = (textureHeight-height) / 2;
+
+        if (false) {
+            // draw a big box for the icon for debugging
+            canvas.drawColor(sColors[mColorIndex]);
+            if (++mColorIndex >= sColors.length) mColorIndex = 0;
+            Paint debugPaint = new Paint();
+            debugPaint.setColor(0xffcccc00);
+            canvas.drawRect(left, top, left+width, top+height, debugPaint);
+        }
+
+        mOldBounds.set(icon.getBounds());
+        icon.setBounds(left, top, left+width, top+height);
+        icon.draw(canvas);
+        icon.setBounds(mOldBounds);
+
+        return bitmap;
+    }
+
+    private Bitmap createSelectedBitmap(Bitmap src, boolean pressed) {
+        final Bitmap result = Bitmap.createBitmap(mIconTextureWidth, mIconTextureHeight,
+                Bitmap.Config.ARGB_8888);
+        final Canvas dest = new Canvas(result);
+
+        dest.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        int[] xy = new int[2];
+        Bitmap mask = src.extractAlpha(mBlurPaint, xy);
+
+        dest.drawBitmap(mask, xy[0], xy[1],
+                pressed ? mGlowColorPressedPaint : mGlowColorFocusedPaint);
+
+        mask.recycle();
+
+        dest.drawBitmap(src, 0, 0, mPaint);
+
+        return result;
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardScreen.java b/policy/com/android/internal/policy/impl/KeyguardScreen.java
new file mode 100644
index 0000000..bbb6875
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardScreen.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+/**
+ * Common interface of each {@link android.view.View} that is a screen of
+ * {@link LockPatternKeyguardView}.
+ */
+public interface KeyguardScreen {
+
+    /**
+     * Return true if your view needs input, so should allow the soft
+     * keyboard to be displayed.
+     */
+    boolean needsInput();
+    
+    /**
+     * This screen is no longer in front of the user.
+     */
+    void onPause();
+
+    /**
+     * This screen is going to be in front of the user.
+     */
+    void onResume();
+
+    /**
+     * This view is going away; a hook to do cleanup.
+     */
+    void cleanUp();
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java b/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java
new file mode 100644
index 0000000..a843603
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardScreenCallback.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.res.Configuration;
+
+/**
+ * Within a keyguard, there may be several screens that need a callback
+ * to the host keyguard view.
+ */
+public interface KeyguardScreenCallback extends KeyguardViewCallback {
+
+    /**
+     * Transition to the lock screen.
+     */
+    void goToLockScreen();
+
+    /**
+     * Transition to the unlock screen.
+     */
+    void goToUnlockScreen();
+
+    /**
+     * The user reported that they forgot their pattern (or not, when they want to back out of the
+     * forgot pattern screen).
+     *
+     * @param isForgotten True if the user hit the forgot pattern, false if they want to back out
+     *        of the account screen.
+     */
+    void forgotPattern(boolean isForgotten);
+
+    /**
+     * @return Whether the keyguard requires some sort of PIN.
+     */
+    boolean isSecure();
+
+    /**
+     * @return Whether we are in a mode where we only want to verify the
+     *   user can get past the keyguard.
+     */
+    boolean isVerifyUnlockOnly();
+
+    /**
+     * Stay on me, but recreate me (so I can use a different layout).
+     */
+    void recreateMe(Configuration config);
+
+    /**
+     * Take action to send an emergency call.
+     */
+    void takeEmergencyCallAction();
+
+    /**
+     * Report that the user had a failed attempt to unlock with password or pattern.
+     */
+    void reportFailedUnlockAttempt();
+
+    /**
+     * Report that the user successfully entered their password or pattern.
+     */
+    void reportSuccessfulUnlockAttempt();
+
+    /**
+     * Report whether we there's another way to unlock the device.
+     * @return true
+     */
+    boolean doesFallbackUnlockScreenExist();
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
new file mode 100644
index 0000000..b225e56
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.database.ContentObserver;
+import static android.os.BatteryManager.BATTERY_STATUS_CHARGING;
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import android.media.AudioManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.provider.Telephony;
+import static android.provider.Telephony.Intents.EXTRA_PLMN;
+import static android.provider.Telephony.Intents.EXTRA_SHOW_PLMN;
+import static android.provider.Telephony.Intents.EXTRA_SHOW_SPN;
+import static android.provider.Telephony.Intents.EXTRA_SPN;
+import static android.provider.Telephony.Intents.SPN_STRINGS_UPDATED_ACTION;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import com.android.internal.R;
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Watches for updates that may be interesting to the keyguard, and provides
+ * the up to date information as well as a registration for callbacks that care
+ * to be updated.
+ *
+ * Note: under time crunch, this has been extended to include some stuff that
+ * doesn't really belong here.  see {@link #handleBatteryUpdate} where it shutdowns
+ * the device, and {@link #getFailedAttempts()}, {@link #reportFailedAttempt()}
+ * and {@link #clearFailedAttempts()}.  Maybe we should rename this 'KeyguardContext'...
+ */
+public class KeyguardUpdateMonitor {
+
+    static private final String TAG = "KeyguardUpdateMonitor";
+    static private final boolean DEBUG = false;
+
+    private static final int LOW_BATTERY_THRESHOLD = 20;
+
+    private final Context mContext;
+
+    private IccCard.State mSimState = IccCard.State.READY;
+
+    private boolean mKeyguardBypassEnabled;
+
+    private boolean mDevicePluggedIn;
+
+    private boolean mDeviceProvisioned;
+
+    private int mBatteryLevel;
+
+    private CharSequence mTelephonyPlmn;
+    private CharSequence mTelephonySpn;
+
+    private int mFailedAttempts = 0;
+
+    private Handler mHandler;
+
+    private ArrayList<InfoCallback> mInfoCallbacks = Lists.newArrayList();
+    private ArrayList<SimStateCallback> mSimStateCallbacks = Lists.newArrayList();
+    private ContentObserver mContentObserver;
+
+    // messages for the handler
+    private static final int MSG_TIME_UPDATE = 301;
+    private static final int MSG_BATTERY_UPDATE = 302;
+    private static final int MSG_CARRIER_INFO_UPDATE = 303;
+    private static final int MSG_SIM_STATE_CHANGE = 304;
+    private static final int MSG_RINGER_MODE_CHANGED = 305;
+    private static final int MSG_PHONE_STATE_CHANGED = 306;
+
+
+    /**
+     * When we receive a
+     * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast,
+     * and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange},
+     * we need a single object to pass to the handler.  This class helps decode
+     * the intent and provide a {@link SimCard.State} result.
+     */
+    private static class SimArgs {
+
+        public final IccCard.State simState;
+
+        private SimArgs(Intent intent) {
+            if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+                throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED");
+            }
+            String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+            if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+                this.simState = IccCard.State.ABSENT;
+            } else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+                this.simState = IccCard.State.READY;
+            } else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+                final String lockedReason = intent
+                        .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+                if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+                    this.simState = IccCard.State.PIN_REQUIRED;
+                } else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+                    this.simState = IccCard.State.PUK_REQUIRED;
+                } else {
+                    this.simState = IccCard.State.UNKNOWN;
+                }
+            } else if (IccCard.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) {
+                this.simState = IccCard.State.NETWORK_LOCKED;
+            } else {
+                this.simState = IccCard.State.UNKNOWN;
+            }
+        }
+
+        public String toString() {
+            return simState.toString();
+        }
+    }
+
+    public KeyguardUpdateMonitor(Context context) {
+        mContext = context;
+
+        mHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_TIME_UPDATE:
+                        handleTimeUpdate();
+                        break;
+                    case MSG_BATTERY_UPDATE:
+                        handleBatteryUpdate(msg.arg1,  msg.arg2);
+                        break;
+                    case MSG_CARRIER_INFO_UPDATE:
+                        handleCarrierInfoUpdate();
+                        break;
+                    case MSG_SIM_STATE_CHANGE:
+                        handleSimStateChange((SimArgs) msg.obj);
+                        break;
+                    case MSG_RINGER_MODE_CHANGED:
+                        handleRingerModeChange(msg.arg1);
+                        break;
+                    case MSG_PHONE_STATE_CHANGED:
+                        handlePhoneStateChanged((String)msg.obj);
+                        break;
+                }
+            }
+        };
+
+        mKeyguardBypassEnabled = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_bypass_keyguard_if_slider_open);
+
+        mDeviceProvisioned = Settings.Secure.getInt(
+                mContext.getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
+
+        // Since device can't be un-provisioned, we only need to register a content observer
+        // to update mDeviceProvisioned when we are...
+        if (!mDeviceProvisioned) {
+            mContentObserver = new ContentObserver(mHandler) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    super.onChange(selfChange);
+                    mDeviceProvisioned = Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
+                    if (mDeviceProvisioned && mContentObserver != null) {
+                        // We don't need the observer anymore...
+                        mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+                        mContentObserver = null;
+                    }
+                    if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned);
+                }
+            };
+
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.DEVICE_PROVISIONED),
+                    false, mContentObserver);
+
+            // prevent a race condition between where we check the flag and where we register the
+            // observer by grabbing the value once again...
+            mDeviceProvisioned = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
+        }
+
+        // take a guess to start
+        mSimState = IccCard.State.READY;
+        mDevicePluggedIn = true;
+        mBatteryLevel = 100;
+
+        mTelephonyPlmn = getDefaultPlmn();
+
+        // setup receiver
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_TIME_TICK);
+        filter.addAction(Intent.ACTION_TIME_CHANGED);
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+        filter.addAction(SPN_STRINGS_UPDATED_ACTION);
+        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        context.registerReceiver(new BroadcastReceiver() {
+
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                if (DEBUG) Log.d(TAG, "received broadcast " + action);
+
+                if (Intent.ACTION_TIME_TICK.equals(action)
+                        || Intent.ACTION_TIME_CHANGED.equals(action)
+                        || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE));
+                } else if (SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+                    mTelephonyPlmn = getTelephonyPlmnFrom(intent);
+                    mTelephonySpn = getTelephonySpnFrom(intent);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE));
+                } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                    final int pluggedInStatus = intent
+                            .getIntExtra("status", BATTERY_STATUS_UNKNOWN);
+                    int batteryLevel = intent.getIntExtra("level", 0);
+                    final Message msg = mHandler.obtainMessage(
+                            MSG_BATTERY_UPDATE,
+                            pluggedInStatus,
+                            batteryLevel);
+                    mHandler.sendMessage(msg);
+                } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
+                    mHandler.sendMessage(mHandler.obtainMessage(
+                            MSG_SIM_STATE_CHANGE,
+                            new SimArgs(intent)));
+                } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED,
+                            intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
+                } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
+                    String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
+                }
+            }
+        }, filter);
+    }
+
+    protected void handlePhoneStateChanged(String newState) {
+        if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")");
+        for (int i = 0; i < mInfoCallbacks.size(); i++) {
+            mInfoCallbacks.get(i).onPhoneStateChanged(newState);
+        }
+    }
+
+    protected void handleRingerModeChange(int mode) {
+        if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")");
+        for (int i = 0; i < mInfoCallbacks.size(); i++) {
+            mInfoCallbacks.get(i).onRingerModeChanged(mode);
+        }
+    }
+
+    /**
+     * Handle {@link #MSG_TIME_UPDATE}
+     */
+    private void handleTimeUpdate() {
+        if (DEBUG) Log.d(TAG, "handleTimeUpdate");
+        for (int i = 0; i < mInfoCallbacks.size(); i++) {
+            mInfoCallbacks.get(i).onTimeChanged();
+        }
+    }
+
+    /**
+     * Handle {@link #MSG_BATTERY_UPDATE}
+     */
+    private void handleBatteryUpdate(int pluggedInStatus, int batteryLevel) {
+        if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
+        final boolean pluggedIn = isPluggedIn(pluggedInStatus);
+
+        if (isBatteryUpdateInteresting(pluggedIn, batteryLevel)) {
+            mBatteryLevel = batteryLevel;
+            mDevicePluggedIn = pluggedIn;
+            for (int i = 0; i < mInfoCallbacks.size(); i++) {
+                mInfoCallbacks.get(i).onRefreshBatteryInfo(
+                        shouldShowBatteryInfo(), pluggedIn, batteryLevel);
+            }
+        }
+    }
+
+    /**
+     * Handle {@link #MSG_CARRIER_INFO_UPDATE}
+     */
+    private void handleCarrierInfoUpdate() {
+        if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn
+            + ", spn = " + mTelephonySpn);
+
+        for (int i = 0; i < mInfoCallbacks.size(); i++) {
+            mInfoCallbacks.get(i).onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+        }
+    }
+
+    /**
+     * Handle {@link #MSG_SIM_STATE_CHANGE}
+     */
+    private void handleSimStateChange(SimArgs simArgs) {
+        final IccCard.State state = simArgs.simState;
+
+        if (DEBUG) {
+            Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " "
+                    + "state resolved to " + state.toString());
+        }
+
+        if (state != IccCard.State.UNKNOWN && state != mSimState) {
+            mSimState = state;
+            for (int i = 0; i < mSimStateCallbacks.size(); i++) {
+                mSimStateCallbacks.get(i).onSimStateChanged(state);
+            }
+        }
+    }
+
+    /**
+     * @param status One of the statuses of {@link android.os.BatteryManager}
+     * @return Whether the status maps to a status for being plugged in.
+     */
+    private boolean isPluggedIn(int status) {
+        return status == BATTERY_STATUS_CHARGING || status == BATTERY_STATUS_FULL;
+    }
+
+    private boolean isBatteryUpdateInteresting(boolean pluggedIn, int batteryLevel) {
+        // change in plug is always interesting
+        if (mDevicePluggedIn != pluggedIn) {
+            return true;
+        }
+
+        // change in battery level while plugged in
+        if (pluggedIn && mBatteryLevel != batteryLevel) {
+            return true;
+        }
+
+        if (!pluggedIn) {
+            // not plugged in and below threshold
+            if (batteryLevel < LOW_BATTERY_THRESHOLD && batteryLevel != mBatteryLevel) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
+     * @return The string to use for the plmn, or null if it should not be shown.
+     */
+    private CharSequence getTelephonyPlmnFrom(Intent intent) {
+        if (intent.getBooleanExtra(EXTRA_SHOW_PLMN, false)) {
+            final String plmn = intent.getStringExtra(EXTRA_PLMN);
+            if (plmn != null) {
+                return plmn;
+            } else {
+                return getDefaultPlmn();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return The default plmn (no service)
+     */
+    private CharSequence getDefaultPlmn() {
+        return mContext.getResources().getText(
+                        R.string.lockscreen_carrier_default);
+    }
+
+    /**
+     * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION}
+     * @return The string to use for the plmn, or null if it should not be shown.
+     */
+    private CharSequence getTelephonySpnFrom(Intent intent) {
+        if (intent.getBooleanExtra(EXTRA_SHOW_SPN, false)) {
+            final String spn = intent.getStringExtra(EXTRA_SPN);
+            if (spn != null) {
+                return spn;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Remove the given observer from being registered from any of the kinds
+     * of callbacks.
+     * @param observer The observer to remove (an instance of {@link ConfigurationChangeCallback},
+     *   {@link InfoCallback} or {@link SimStateCallback}
+     */
+    public void removeCallback(Object observer) {
+        mInfoCallbacks.remove(observer);
+        mSimStateCallbacks.remove(observer);
+    }
+
+    /**
+     * Callback for general information relevant to lock screen.
+     */
+    interface InfoCallback {
+        void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel);
+        void onTimeChanged();
+
+        /**
+         * @param plmn The operator name of the registered network.  May be null if it shouldn't
+         *   be displayed.
+         * @param spn The service provider name.  May be null if it shouldn't be displayed.
+         */
+        void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn);
+
+        /**
+         * Called when the ringer mode changes.
+         * @param state the current ringer state, as defined in
+         * {@link AudioManager#RINGER_MODE_CHANGED_ACTION}
+         */
+        void onRingerModeChanged(int state);
+
+        /**
+         * Called when the phone state changes. String will be one of:
+         * {@link TelephonyManager#EXTRA_STATE_IDLE}
+         * {@link TelephonyManager@EXTRA_STATE_RINGING}
+         * {@link TelephonyManager#EXTRA_STATE_OFFHOOK
+         */
+        void onPhoneStateChanged(String newState);
+    }
+
+    /**
+     * Callback to notify of sim state change.
+     */
+    interface SimStateCallback {
+        void onSimStateChanged(IccCard.State simState);
+    }
+
+    /**
+     * Register to receive notifications about general keyguard information
+     * (see {@link InfoCallback}.
+     * @param callback The callback.
+     */
+    public void registerInfoCallback(InfoCallback callback) {
+        if (!mInfoCallbacks.contains(callback)) {
+            mInfoCallbacks.add(callback);
+        } else {
+            Log.e(TAG, "Object tried to add another INFO callback", new Exception("Whoops"));
+        }
+    }
+
+    /**
+     * Register to be notified of sim state changes.
+     * @param callback The callback.
+     */
+    public void registerSimStateCallback(SimStateCallback callback) {
+        if (!mSimStateCallbacks.contains(callback)) {
+            mSimStateCallbacks.add(callback);
+        } else {
+            Log.e(TAG, "Object tried to add another SIM callback", new Exception("Whoops"));
+        }
+    }
+
+    public IccCard.State getSimState() {
+        return mSimState;
+    }
+
+    /**
+     * Report that the user succesfully entered the sim pin so we
+     * have the information earlier than waiting for the intent
+     * broadcast from the telephony code.
+     */
+    public void reportSimPinUnlocked() {
+        mSimState = IccCard.State.READY;
+    }
+
+    public boolean isKeyguardBypassEnabled() {
+        return mKeyguardBypassEnabled;
+    }
+
+    public boolean isDevicePluggedIn() {
+        return mDevicePluggedIn;
+    }
+
+    public int getBatteryLevel() {
+        return mBatteryLevel;
+    }
+
+    public boolean shouldShowBatteryInfo() {
+        return mDevicePluggedIn || mBatteryLevel < LOW_BATTERY_THRESHOLD;
+    }
+
+    public CharSequence getTelephonyPlmn() {
+        return mTelephonyPlmn;
+    }
+
+    public CharSequence getTelephonySpn() {
+        return mTelephonySpn;
+    }
+
+    /**
+     * @return Whether the device is provisioned (whether they have gone through
+     *   the setup wizard)
+     */
+    public boolean isDeviceProvisioned() {
+        return mDeviceProvisioned;
+    }
+
+    public int getFailedAttempts() {
+        return mFailedAttempts;
+    }
+
+    public void clearFailedAttempts() {
+        mFailedAttempts = 0;
+    }
+
+    public void reportFailedAttempt() {
+        mFailedAttempts++;
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/com/android/internal/policy/impl/KeyguardViewBase.java
new file mode 100644
index 0000000..9dcbcb6
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardViewBase.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.telephony.TelephonyManager;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Gravity;
+import android.widget.FrameLayout;
+import android.util.AttributeSet;
+
+/**
+ * Base class for keyguard views.  {@link #reset} is where you should
+ * reset the state of your view.  Use the {@link KeyguardViewCallback} via
+ * {@link #getCallback()} to send information back (such as poking the wake lock,
+ * or finishing the keyguard).
+ *
+ * Handles intercepting of media keys that still work when the keyguard is
+ * showing.
+ */
+public abstract class KeyguardViewBase extends FrameLayout {
+
+    private KeyguardViewCallback mCallback;
+    private AudioManager mAudioManager;
+    private TelephonyManager mTelephonyManager = null;
+
+    public KeyguardViewBase(Context context) {
+        super(context);
+
+        // drop shadow below status bar in keyguard too
+        mForegroundInPadding = false;
+        setForegroundGravity(Gravity.FILL_HORIZONTAL | Gravity.TOP);
+        setForeground(
+                context.getResources().getDrawable(
+                        com.android.internal.R.drawable.title_bar_shadow));
+    }
+
+    // used to inject callback
+    void setCallback(KeyguardViewCallback callback) {
+        mCallback = callback;
+    }
+
+    public KeyguardViewCallback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * Called when you need to reset the state of your view.
+     */
+    abstract public void reset();
+
+    /**
+     * Called when the screen turned off.
+     */
+    abstract public void onScreenTurnedOff();
+
+    /**
+     * Called when the screen turned on.
+     */
+    abstract public void onScreenTurnedOn();
+
+    /**
+     * Called when a key has woken the device to give us a chance to adjust our
+     * state according the the key.  We are responsible for waking the device
+     * (by poking the wake lock) once we are ready.
+     *
+     * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
+     * Be sure not to take any action that takes a long time; any significant
+     * action should be posted to a handler.
+     *
+     * @param keyCode The wake key, which may be relevant for configuring the
+     *   keyguard.
+     */
+    abstract public void wakeWhenReadyTq(int keyCode);
+
+    /**
+     * Verify that the user can get past the keyguard securely.  This is called,
+     * for example, when the phone disables the keyguard but then wants to launch
+     * something else that requires secure access.
+     *
+     * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
+     */
+    abstract public void verifyUnlock();
+
+    /**
+     * Called before this view is being removed.
+     */
+    abstract public void cleanUp();
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (shouldEventKeepScreenOnWhileKeyguardShowing(event)) {
+            mCallback.pokeWakelock();
+        }
+
+        if (interceptMediaKey(event)) {
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    private boolean shouldEventKeepScreenOnWhileKeyguardShowing(KeyEvent event) {
+        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+            return false;
+        }
+        switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_UP:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    /**
+     * Allows the media keys to work when the keyguard is showing.
+     * The media keys should be of no interest to the actual keyguard view(s),
+     * so intercepting them here should not be of any harm.
+     * @param event The key event
+     * @return whether the event was consumed as a media key.
+     */
+    private boolean interceptMediaKey(KeyEvent event) {
+        final int keyCode = event.getKeyCode();
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                    /* Suppress PLAYPAUSE toggle when phone is ringing or
+                     * in-call to avoid music playback */
+                    if (mTelephonyManager == null) {
+                        mTelephonyManager = (TelephonyManager) getContext().getSystemService(
+                                Context.TELEPHONY_SERVICE);
+                    }
+                    if (mTelephonyManager != null &&
+                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
+                        return true;  // suppress key event
+                    }
+                case KeyEvent.KEYCODE_HEADSETHOOK: 
+                case KeyEvent.KEYCODE_MEDIA_STOP: 
+                case KeyEvent.KEYCODE_MEDIA_NEXT: 
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 
+                case KeyEvent.KEYCODE_MEDIA_REWIND: 
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                    intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+                    getContext().sendOrderedBroadcast(intent, null);
+                    return true;
+                }
+
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                case KeyEvent.KEYCODE_VOLUME_DOWN: {
+                    synchronized (this) {
+                        if (mAudioManager == null) {
+                            mAudioManager = (AudioManager) getContext().getSystemService(
+                                    Context.AUDIO_SERVICE);
+                        }
+                    }
+                    // Volume buttons should only function for music.
+                    if (mAudioManager.isMusicActive()) {
+                        mAudioManager.adjustStreamVolume(
+                                    AudioManager.STREAM_MUSIC,
+                                    keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                                            ? AudioManager.ADJUST_RAISE
+                                            : AudioManager.ADJUST_LOWER,
+                                    0);
+                    }
+                    // Don't execute default volume behavior
+                    return true;
+                }
+            }
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_MUTE:
+                case KeyEvent.KEYCODE_HEADSETHOOK: 
+                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 
+                case KeyEvent.KEYCODE_MEDIA_STOP: 
+                case KeyEvent.KEYCODE_MEDIA_NEXT: 
+                case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 
+                case KeyEvent.KEYCODE_MEDIA_REWIND: 
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                    intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+                    getContext().sendOrderedBroadcast(intent, null);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewCallback.java b/policy/com/android/internal/policy/impl/KeyguardViewCallback.java
new file mode 100644
index 0000000..b376d65
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardViewCallback.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+/**
+ * The callback used by the keyguard view to tell the {@link KeyguardViewMediator} 
+ * various things.
+ */
+public interface KeyguardViewCallback {
+
+    /**
+     * Request the wakelock to be poked for the default amount of time.
+     */
+    void pokeWakelock();
+
+    /**
+     * Request the wakelock to be poked for a specific amount of time.
+     * @param millis The amount of time in millis.
+     */
+    void pokeWakelock(int millis);
+
+    /**
+     * Report that the keyguard is done.
+     * @param authenticated Whether the user securely got past the keyguard.
+     *   the only reason for this to be false is if the keyguard was instructed
+     *   to appear temporarily to verify the user is supposed to get past the
+     *   keyguard, and the user fails to do so.
+     */
+    void keyguardDone(boolean authenticated);
+
+    /**
+     * Report that the keyguard is done drawing.
+     */
+    void keyguardDoneDrawing();
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/com/android/internal/policy/impl/KeyguardViewManager.java
new file mode 100644
index 0000000..ba1d7f5
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+import com.android.internal.R;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.graphics.Canvas;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewManager;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+/**
+ * Manages creating, showing, hiding and resetting the keyguard.  Calls back
+ * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke
+ * the wake lock and report that the keyguard is done, which is in turn,
+ * reported to this class by the current {@link KeyguardViewBase}.
+ */
+public class KeyguardViewManager implements KeyguardWindowController {
+    private final static boolean DEBUG = false;
+    private static String TAG = "KeyguardViewManager";
+
+    private final Context mContext;
+    private final ViewManager mViewManager;
+    private final KeyguardViewCallback mCallback;
+    private final KeyguardViewProperties mKeyguardViewProperties;
+
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+
+    private WindowManager.LayoutParams mWindowLayoutParams;
+    private boolean mNeedsInput = false;
+
+    private FrameLayout mKeyguardHost;
+    private KeyguardViewBase mKeyguardView;
+
+    private boolean mScreenOn = false;
+
+    /**
+     * @param context Used to create views.
+     * @param viewManager Keyguard will be attached to this.
+     * @param callback Used to notify of changes.
+     */
+    public KeyguardViewManager(Context context, ViewManager viewManager,
+            KeyguardViewCallback callback, KeyguardViewProperties keyguardViewProperties, KeyguardUpdateMonitor updateMonitor) {
+        mContext = context;
+        mViewManager = viewManager;
+        mCallback = callback;
+        mKeyguardViewProperties = keyguardViewProperties;
+
+        mUpdateMonitor = updateMonitor;
+    }
+
+    /**
+     * Helper class to host the keyguard view.
+     */
+    private static class KeyguardViewHost extends FrameLayout {
+        private final KeyguardViewCallback mCallback;
+
+        private KeyguardViewHost(Context context, KeyguardViewCallback callback) {
+            super(context);
+            mCallback = callback;
+        }
+
+        @Override
+        protected void dispatchDraw(Canvas canvas) {
+            super.dispatchDraw(canvas);
+            mCallback.keyguardDoneDrawing();
+        }
+    }
+
+    /**
+     * Show the keyguard.  Will handle creating and attaching to the view manager
+     * lazily.
+     */
+    public synchronized void show() {
+        if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView);
+
+        if (mKeyguardHost == null) {
+            if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");
+
+            mKeyguardHost = new KeyguardViewHost(mContext, mCallback);
+
+            final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
+            int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
+                    | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
+                    | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
+                    /*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ;
+            if (!mNeedsInput) {
+                flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+            }
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD,
+                    flags, PixelFormat.TRANSLUCENT);
+            lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+            lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;
+            lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+            lp.setTitle("Keyguard");
+            mWindowLayoutParams = lp;
+
+            mViewManager.addView(mKeyguardHost, lp);
+        }
+
+        if (mKeyguardView == null) {
+            if (DEBUG) Log.d(TAG, "keyguard view is null, creating it...");
+            mKeyguardView = mKeyguardViewProperties.createKeyguardView(mContext, mUpdateMonitor, this);
+            mKeyguardView.setId(R.id.lock_screen);
+            mKeyguardView.setCallback(mCallback);
+
+            final ViewGroup.LayoutParams lp = new FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT);
+
+            mKeyguardHost.addView(mKeyguardView, lp);
+
+            if (mScreenOn) {
+                mKeyguardView.onScreenTurnedOn();
+            }
+        }
+
+        mKeyguardHost.setVisibility(View.VISIBLE);
+        mKeyguardView.requestFocus();
+    }
+
+    public void setNeedsInput(boolean needsInput) {
+        mNeedsInput = needsInput;
+        if (mWindowLayoutParams != null) {
+            if (needsInput) {
+                mWindowLayoutParams.flags &=
+                    ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+            } else {
+                mWindowLayoutParams.flags |=
+                    WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+            }
+            mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
+        }
+    }
+
+    /**
+     * Reset the state of the view.
+     */
+    public synchronized void reset() {
+        if (DEBUG) Log.d(TAG, "reset()");
+        if (mKeyguardView != null) {
+            mKeyguardView.reset();
+        }
+    }
+
+    public synchronized void onScreenTurnedOff() {
+        if (DEBUG) Log.d(TAG, "onScreenTurnedOff()");
+        mScreenOn = false;
+        if (mKeyguardView != null) {
+            mKeyguardView.onScreenTurnedOff();
+        }
+    }
+
+    public synchronized void onScreenTurnedOn() {
+        if (DEBUG) Log.d(TAG, "onScreenTurnedOn()");
+        mScreenOn = true;
+        if (mKeyguardView != null) {
+            mKeyguardView.onScreenTurnedOn();
+        }
+    }
+
+    public synchronized void verifyUnlock() {
+        if (DEBUG) Log.d(TAG, "verifyUnlock()");
+        show();
+        mKeyguardView.verifyUnlock();
+    }
+
+    /**
+     * A key has woken the device.  We use this to potentially adjust the state
+     * of the lock screen based on the key.
+     *
+     * The 'Tq' suffix is per the documentation in {@link android.view.WindowManagerPolicy}.
+     * Be sure not to take any action that takes a long time; any significant
+     * action should be posted to a handler.
+     *
+     * @param keyCode The wake key.
+     */
+    public boolean wakeWhenReadyTq(int keyCode) {
+        if (DEBUG) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
+        if (mKeyguardView != null) {
+            mKeyguardView.wakeWhenReadyTq(keyCode);
+            return true;
+        } else {
+            Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq");
+            return false;
+        }
+    }
+
+    /**
+     * Hides the keyguard view
+     */
+    public synchronized void hide() {
+        if (DEBUG) Log.d(TAG, "hide()");
+        if (mKeyguardHost != null) {
+            mKeyguardHost.setVisibility(View.GONE);
+            // Don't do this right away, so we can let the view continue to animate
+            // as it goes away.
+            if (mKeyguardView != null) {
+                final KeyguardViewBase lastView = mKeyguardView;
+                mKeyguardView = null;
+                mKeyguardHost.postDelayed(new Runnable() {
+                    public void run() {
+                        synchronized (KeyguardViewManager.this) {
+                            mKeyguardHost.removeView(lastView);
+                            lastView.cleanUp();
+                        }
+                    }
+                }, 500);
+            }
+        }
+    }
+
+    /**
+     * @return Whether the keyguard is showing
+     */
+    public synchronized boolean isShowing() {
+        return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/com/android/internal/policy/impl/KeyguardViewMediator.java
new file mode 100644
index 0000000..c255041
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -0,0 +1,1135 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+import com.android.internal.telephony.IccCard;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.LocalPowerManager;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.Config;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.WindowManagerImpl;
+import android.view.WindowManagerPolicy;
+
+
+/**
+ * Mediates requests related to the keyguard.  This includes queries about the
+ * state of the keyguard, power management events that effect whether the keyguard
+ * should be shown or reset, callbacks to the phone window manager to notify
+ * it of when the keyguard is showing, and events from the keyguard view itself
+ * stating that the keyguard was succesfully unlocked.
+ *
+ * Note that the keyguard view is shown when the screen is off (as appropriate)
+ * so that once the screen comes on, it will be ready immediately.
+ *
+ * Example queries about the keyguard:
+ * - is {movement, key} one that should wake the keygaurd?
+ * - is the keyguard showing?
+ * - are input events restricted due to the state of the keyguard?
+ *
+ * Callbacks to the phone window manager:
+ * - the keyguard is showing
+ *
+ * Example external events that translate to keyguard view changes:
+ * - screen turned off -> reset the keyguard, and show it so it will be ready
+ *   next time the screen turns on
+ * - keyboard is slid open -> if the keyguard is not secure, hide it
+ *
+ * Events from the keyguard view:
+ * - user succesfully unlocked keyguard -> hide keyguard view, and no longer
+ *   restrict input events.
+ *
+ * Note: in addition to normal power managment events that effect the state of
+ * whether the keyguard should be showing, external apps and services may request
+ * that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}.  When
+ * false, this will override all other conditions for turning on the keyguard.
+ *
+ * Threading and synchronization:
+ * This class is created by the initialization routine of the {@link WindowManagerPolicy},
+ * and runs on its thread.  The keyguard UI is created from that thread in the
+ * constructor of this class.  The apis may be called from other threads, including the
+ * {@link com.android.server.KeyInputQueue}'s and {@link android.view.WindowManager}'s.
+ * Therefore, methods on this class are synchronized, and any action that is pointed
+ * directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI
+ * thread of the keyguard.
+ */
+public class KeyguardViewMediator implements KeyguardViewCallback,
+        KeyguardUpdateMonitor.SimStateCallback {
+    private final static boolean DEBUG = false && Config.LOGD;
+    private final static boolean DBG_WAKE = DEBUG || true;
+
+    private final static String TAG = "KeyguardViewMediator";
+
+    private static final String DELAYED_KEYGUARD_ACTION =
+        "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD";
+
+    // used for handler messages
+    private static final int TIMEOUT = 1;
+    private static final int SHOW = 2;
+    private static final int HIDE = 3;
+    private static final int RESET = 4;
+    private static final int VERIFY_UNLOCK = 5;
+    private static final int NOTIFY_SCREEN_OFF = 6;
+    private static final int NOTIFY_SCREEN_ON = 7;
+    private static final int WAKE_WHEN_READY = 8;
+    private static final int KEYGUARD_DONE = 9;
+    private static final int KEYGUARD_DONE_DRAWING = 10;
+    private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
+    private static final int SET_HIDDEN = 12;
+    private static final int KEYGUARD_TIMEOUT = 13;
+
+    /**
+     * The default amount of time we stay awake (used for all key input)
+     */
+    protected static final int AWAKE_INTERVAL_DEFAULT_MS = 5000;
+
+
+    /**
+     * The default amount of time we stay awake (used for all key input) when
+     * the keyboard is open
+     */
+    protected static final int AWAKE_INTERVAL_DEFAULT_KEYBOARD_OPEN_MS = 10000;
+
+    /**
+     * How long to wait after the screen turns off due to timeout before
+     * turning on the keyguard (i.e, the user has this much time to turn
+     * the screen back on without having to face the keyguard).
+     */
+    private static final int KEYGUARD_DELAY_MS = 5000;
+
+    /**
+     * How long we'll wait for the {@link KeyguardViewCallback#keyguardDoneDrawing()}
+     * callback before unblocking a call to {@link #setKeyguardEnabled(boolean)}
+     * that is reenabling the keyguard.
+     */
+    private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
+
+    private Context mContext;
+    private AlarmManager mAlarmManager;
+    private StatusBarManager mStatusBarManager;
+    private boolean mShowLockIcon = false;
+    private IBinder mSecureLockIcon = null;
+
+    private boolean mSystemReady;
+
+    // Whether the next call to playSounds() should be skipped.  Defaults to
+    // true because the first lock (on boot) should be silent.
+    private boolean mSuppressNextLockSound = true;
+
+
+    /** Low level access to the power manager for enableUserActivity.  Having this
+     * requires that we run in the system process.  */
+    LocalPowerManager mRealPowerManager;
+
+    /** High level access to the power manager for WakeLocks */
+    private PowerManager mPM;
+
+    /**
+     * Used to keep the device awake while the keyguard is showing, i.e for
+     * calls to {@link #pokeWakelock()}
+     */
+    private PowerManager.WakeLock mWakeLock;
+
+    /**
+     * Used to keep the device awake while to ensure the keyguard finishes opening before
+     * we sleep.
+     */
+    private PowerManager.WakeLock mShowKeyguardWakeLock;
+
+    /**
+     * Does not turn on screen, held while a call to {@link KeyguardViewManager#wakeWhenReadyTq(int)}
+     * is called to make sure the device doesn't sleep before it has a chance to poke
+     * the wake lock.
+     * @see #wakeWhenReadyLocked(int)
+     */
+    private PowerManager.WakeLock mWakeAndHandOff;
+
+    private KeyguardViewManager mKeyguardViewManager;
+
+    // these are protected by synchronized (this)
+
+    /**
+     * External apps (like the phone app) can tell us to disable the keygaurd.
+     */
+    private boolean mExternallyEnabled = true;
+
+    /**
+     * Remember if an external call to {@link #setKeyguardEnabled} with value
+     * false caused us to hide the keyguard, so that we need to reshow it once
+     * the keygaurd is reenabled with another call with value true.
+     */
+    private boolean mNeedToReshowWhenReenabled = false;
+
+    // cached value of whether we are showing (need to know this to quickly
+    // answer whether the input should be restricted)
+    private boolean mShowing = false;
+
+    // true if the keyguard is hidden by another window
+    private boolean mHidden = false;
+
+    /**
+     * Helps remember whether the screen has turned on since the last time
+     * it turned off due to timeout. see {@link #onScreenTurnedOff(int)}
+     */
+    private int mDelayedShowingSequence;
+
+    private int mWakelockSequence;
+
+    private PhoneWindowManager mCallback;
+
+    /**
+     * If the user has disabled the keyguard, then requests to exit, this is
+     * how we'll ultimately let them know whether it was successful.  We use this
+     * var being non-null as an indicator that there is an in progress request.
+     */
+    private WindowManagerPolicy.OnKeyguardExitResult mExitSecureCallback;
+
+    // the properties of the keyguard
+    private KeyguardViewProperties mKeyguardViewProperties;
+
+    private KeyguardUpdateMonitor mUpdateMonitor;
+
+    private boolean mKeyboardOpen = false;
+
+    private boolean mScreenOn = false;
+
+    // last known state of the cellular connection
+    private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
+
+    /**
+     * we send this intent when the keyguard is dismissed.
+     */
+    private Intent mUserPresentIntent;
+
+    /**
+     * {@link #setKeyguardEnabled} waits on this condition when it reenables
+     * the keyguard.
+     */
+    private boolean mWaitingUntilKeyguardVisible = false;
+
+    public KeyguardViewMediator(Context context, PhoneWindowManager callback,
+            LocalPowerManager powerManager) {
+        mContext = context;
+
+        mRealPowerManager = powerManager;
+        mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = mPM.newWakeLock(
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+                "keyguard");
+        mWakeLock.setReferenceCounted(false);
+        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
+        mShowKeyguardWakeLock.setReferenceCounted(false);
+
+        mWakeAndHandOff = mPM.newWakeLock(
+                PowerManager.PARTIAL_WAKE_LOCK,
+                "keyguardWakeAndHandOff");
+        mWakeAndHandOff.setReferenceCounted(false);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(DELAYED_KEYGUARD_ACTION);
+        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+        context.registerReceiver(mBroadCastReceiver, filter);
+        mAlarmManager = (AlarmManager) context
+                .getSystemService(Context.ALARM_SERVICE);
+        mCallback = callback;
+
+        mUpdateMonitor = new KeyguardUpdateMonitor(context);
+
+        mUpdateMonitor.registerSimStateCallback(this);
+
+        mKeyguardViewProperties = new LockPatternKeyguardViewProperties(
+                new LockPatternUtils(mContext), mUpdateMonitor);
+
+        mKeyguardViewManager = new KeyguardViewManager(
+                context, WindowManagerImpl.getDefault(), this,
+                mKeyguardViewProperties, mUpdateMonitor);
+
+        mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT);
+        mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+        final ContentResolver cr = mContext.getContentResolver();
+        mShowLockIcon = (Settings.System.getInt(cr, "show_status_bar_lock", 0) == 1);
+    }
+
+    /**
+     * Let us know that the system is ready after startup.
+     */
+    public void onSystemReady() {
+        synchronized (this) {
+            if (DEBUG) Log.d(TAG, "onSystemReady");
+            mSystemReady = true;
+            doKeyguard();
+        }
+    }
+
+    /**
+     * Called to let us know the screen was turned off.
+     * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER},
+     *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
+     *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
+     */
+    public void onScreenTurnedOff(int why) {
+        synchronized (this) {
+            mScreenOn = false;
+            if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")");
+
+            if (mExitSecureCallback != null) {
+                if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
+                mExitSecureCallback.onKeyguardExitResult(false);
+                mExitSecureCallback = null;
+                if (!mExternallyEnabled) {
+                    hideLocked();
+                }
+            } else if (mShowing) {
+                notifyScreenOffLocked();
+                resetStateLocked();
+            } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT) {
+                // if the screen turned off because of timeout, set an alarm
+                // to enable it a little bit later (i.e, give the user a chance
+                // to turn the screen back on within a certain window without
+                // having to unlock the screen)
+                long when = SystemClock.elapsedRealtime() + KEYGUARD_DELAY_MS;
+                Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+                intent.putExtra("seq", mDelayedShowingSequence);
+                PendingIntent sender = PendingIntent.getBroadcast(mContext,
+                        0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when,
+                        sender);
+                if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+                                 + mDelayedShowingSequence);
+            } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
+                // Do not enable the keyguard if the prox sensor forced the screen off.
+            } else {
+                doKeyguard();
+            }
+        }
+    }
+
+    /**
+     * Let's us know the screen was turned on.
+     */
+    public void onScreenTurnedOn() {
+        synchronized (this) {
+            mScreenOn = true;
+            mDelayedShowingSequence++;
+            if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
+            notifyScreenOnLocked();
+        }
+    }
+
+    /**
+     * Same semantics as {@link WindowManagerPolicy#enableKeyguard}; provide
+     * a way for external stuff to override normal keyguard behavior.  For instance
+     * the phone app disables the keyguard when it receives incoming calls.
+     */
+    public void setKeyguardEnabled(boolean enabled) {
+        synchronized (this) {
+            if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
+
+
+            mExternallyEnabled = enabled;
+
+            if (!enabled && mShowing) {
+                if (mExitSecureCallback != null) {
+                    if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
+                    // we're in the process of handling a request to verify the user
+                    // can get past the keyguard. ignore extraneous requests to disable / reenable
+                    return;
+                }
+
+                // hiding keyguard that is showing, remember to reshow later
+                if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
+                        + "disabling status bar expansion");
+                mNeedToReshowWhenReenabled = true;
+                hideLocked();
+            } else if (enabled && mNeedToReshowWhenReenabled) {
+                // reenabled after previously hidden, reshow
+                if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
+                        + "status bar expansion");
+                mNeedToReshowWhenReenabled = false;
+
+                if (mExitSecureCallback != null) {
+                    if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
+                    mExitSecureCallback.onKeyguardExitResult(false);
+                    mExitSecureCallback = null;
+                    resetStateLocked();
+                } else {
+                    showLocked();
+
+                    // block until we know the keygaurd is done drawing (and post a message
+                    // to unblock us after a timeout so we don't risk blocking too long
+                    // and causing an ANR).
+                    mWaitingUntilKeyguardVisible = true;
+                    mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
+                    if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false");
+                    while (mWaitingUntilKeyguardVisible) {
+                        try {
+                            wait();
+                        } catch (InterruptedException e) {
+                            Thread.currentThread().interrupt();
+                        }
+                    }
+                    if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible");
+                }
+            }
+        }
+    }
+
+    /**
+     * @see android.app.KeyguardManager#exitKeyguardSecurely
+     */
+    public void verifyUnlock(WindowManagerPolicy.OnKeyguardExitResult callback) {
+        synchronized (this) {
+            if (DEBUG) Log.d(TAG, "verifyUnlock");
+            if (!mUpdateMonitor.isDeviceProvisioned()) {
+                // don't allow this api when the device isn't provisioned
+                if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned");
+                callback.onKeyguardExitResult(false);
+            } else if (mExternallyEnabled) {
+                // this only applies when the user has externally disabled the
+                // keyguard.  this is unexpected and means the user is not
+                // using the api properly.
+                Log.w(TAG, "verifyUnlock called when not externally disabled");
+                callback.onKeyguardExitResult(false);
+            } else if (mExitSecureCallback != null) {
+                // already in progress with someone else
+                callback.onKeyguardExitResult(false);
+            } else {
+                mExitSecureCallback = callback;
+                verifyUnlockLocked();
+            }
+        }
+    }
+
+    /**
+     * Is the keyguard currently showing?
+     */
+    public boolean isShowing() {
+        return mShowing;
+    }
+
+    /**
+     * Is the keyguard currently showing and not being force hidden?
+     */
+    public boolean isShowingAndNotHidden() {
+        return mShowing && !mHidden;
+    }
+
+    /**
+     * Notify us when the keyguard is hidden by another window
+     */
+    public void setHidden(boolean isHidden) {
+        if (DEBUG) Log.d(TAG, "setHidden " + isHidden);
+        mHandler.removeMessages(SET_HIDDEN);
+        Message msg = mHandler.obtainMessage(SET_HIDDEN, (isHidden ? 1 : 0), 0);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Handles SET_HIDDEN message sent by setHidden()
+     */
+    private void handleSetHidden(boolean isHidden) {
+        synchronized (KeyguardViewMediator.this) {
+            if (mHidden != isHidden) {
+                mHidden = isHidden;
+                adjustUserActivityLocked();
+                adjustStatusBarLocked();
+            }
+        }
+    }
+
+    /**
+     * Used by PhoneWindowManager to enable the keyguard due to a user activity timeout.
+     * This must be safe to call from any thread and with any window manager locks held.
+     */
+    public void doKeyguardTimeout() {
+        mHandler.removeMessages(KEYGUARD_TIMEOUT);
+        Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Given the state of the keyguard, is the input restricted?
+     * Input is restricted when the keyguard is showing, or when the keyguard
+     * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
+     */
+    public boolean isInputRestricted() {
+        return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned();
+    }
+
+    /**
+     * Returns true if the change is resulting in the keyguard beign dismissed,
+     * meaning the screen can turn on immediately.  Otherwise returns false.
+     */
+    public boolean doLidChangeTq(boolean isLidOpen) {
+        mKeyboardOpen = isLidOpen;
+
+        if (mUpdateMonitor.isKeyguardBypassEnabled() && mKeyboardOpen
+                && !mKeyguardViewProperties.isSecure() && mKeyguardViewManager.isShowing()) {
+            if (DEBUG) Log.d(TAG, "bypassing keyguard on sliding open of keyboard with non-secure keyguard");
+            mHandler.sendEmptyMessage(KEYGUARD_DONE_AUTHENTICATING);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Enable the keyguard if the settings are appropriate.
+     */
+    private void doKeyguard() {
+        synchronized (this) {
+            // if another app is disabling us, don't show
+            if (!mExternallyEnabled) {
+                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+
+                // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
+                // for an occasional ugly flicker in this situation:
+                // 1) receive a call with the screen on (no keyguard) or make a call
+                // 2) screen times out
+                // 3) user hits key to turn screen back on
+                // instead, we reenable the keyguard when we know the screen is off and the call
+                // ends (see the broadcast receiver below)
+                // TODO: clean this up when we have better support at the window manager level
+                // for apps that wish to be on top of the keyguard
+                return;
+            }
+
+            // if the keyguard is already showing, don't bother
+            if (mKeyguardViewManager.isShowing()) {
+                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+                return;
+            }
+
+            // if the setup wizard hasn't run yet, don't show
+            final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
+                    false);
+            final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
+            final IccCard.State state = mUpdateMonitor.getSimState();
+            final boolean lockedOrMissing = state.isPinLocked()
+                    || ((state == IccCard.State.ABSENT) && requireSim);
+
+            if (!lockedOrMissing && !provisioned) {
+                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+                        + " and the sim is not locked or missing");
+                return;
+            }
+
+            if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+            showLocked();
+        }
+    }
+
+    /**
+     * Send message to keyguard telling it to reset its state.
+     * @see #handleReset()
+     */
+    private void resetStateLocked() {
+        if (DEBUG) Log.d(TAG, "resetStateLocked");
+        Message msg = mHandler.obtainMessage(RESET);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Send message to keyguard telling it to verify unlock
+     * @see #handleVerifyUnlock()
+     */
+    private void verifyUnlockLocked() {
+        if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
+        mHandler.sendEmptyMessage(VERIFY_UNLOCK);
+    }
+
+
+    /**
+     * Send a message to keyguard telling it the screen just turned on.
+     * @see #onScreenTurnedOff(int)
+     * @see #handleNotifyScreenOff
+     */
+    private void notifyScreenOffLocked() {
+        if (DEBUG) Log.d(TAG, "notifyScreenOffLocked");
+        mHandler.sendEmptyMessage(NOTIFY_SCREEN_OFF);
+    }
+
+    /**
+     * Send a message to keyguard telling it the screen just turned on.
+     * @see #onScreenTurnedOn()
+     * @see #handleNotifyScreenOn
+     */
+    private void notifyScreenOnLocked() {
+        if (DEBUG) Log.d(TAG, "notifyScreenOnLocked");
+        mHandler.sendEmptyMessage(NOTIFY_SCREEN_ON);
+    }
+
+    /**
+     * Send message to keyguard telling it about a wake key so it can adjust
+     * its state accordingly and then poke the wake lock when it is ready.
+     * @param keyCode The wake key.
+     * @see #handleWakeWhenReady
+     * @see #onWakeKeyWhenKeyguardShowingTq(int)
+     */
+    private void wakeWhenReadyLocked(int keyCode) {
+        if (DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
+
+        /**
+         * acquire the handoff lock that will keep the cpu running.  this will
+         * be released once the keyguard has set itself up and poked the other wakelock
+         * in {@link #handleWakeWhenReady(int)}
+         */
+        mWakeAndHandOff.acquire();
+
+        Message msg = mHandler.obtainMessage(WAKE_WHEN_READY, keyCode, 0);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Send message to keyguard telling it to show itself
+     * @see #handleShow()
+     */
+    private void showLocked() {
+        if (DEBUG) Log.d(TAG, "showLocked");
+        // ensure we stay awake until we are finished displaying the keyguard
+        mShowKeyguardWakeLock.acquire();
+        Message msg = mHandler.obtainMessage(SHOW);
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Send message to keyguard telling it to hide itself
+     * @see #handleHide()
+     */
+    private void hideLocked() {
+        if (DEBUG) Log.d(TAG, "hideLocked");
+        Message msg = mHandler.obtainMessage(HIDE);
+        mHandler.sendMessage(msg);
+    }
+
+    /** {@inheritDoc} */
+    public void onSimStateChanged(IccCard.State simState) {
+        if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState);
+
+        switch (simState) {
+            case ABSENT:
+                // only force lock screen in case of missing sim if user hasn't
+                // gone through setup wizard
+                if (!mUpdateMonitor.isDeviceProvisioned()) {
+                    if (!isShowing()) {
+                        if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_ABSENT and keygaurd isn't showing, we need "
+                             + "to show the keyguard since the device isn't provisioned yet.");
+                        doKeyguard();
+                    } else {
+                        resetStateLocked();
+                    }
+                }
+                break;
+            case PIN_REQUIRED:
+            case PUK_REQUIRED:
+                if (!isShowing()) {
+                    if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
+                            + "to show the keyguard so the user can enter their sim pin");
+                    doKeyguard();
+                } else {
+                    resetStateLocked();
+                }
+
+                break;
+            case READY:
+                if (isShowing()) {
+                    resetStateLocked();
+                }
+                break;
+        }
+    }
+
+    public boolean isSecure() {
+        return mKeyguardViewProperties.isSecure();
+    }
+
+    private BroadcastReceiver mBroadCastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(DELAYED_KEYGUARD_ACTION)) {
+
+                int sequence = intent.getIntExtra("seq", 0);
+
+                if (false) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
+                        + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
+
+                if (mDelayedShowingSequence == sequence) {
+                    // Don't play lockscreen SFX if the screen went off due to
+                    // timeout.
+                    mSuppressNextLockSound = true;
+
+                    doKeyguard();
+                }
+            } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
+                mPhoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
+
+                if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)  // call ending
+                        && !mScreenOn                           // screen off
+                        && mExternallyEnabled) {                // not disabled by any app
+
+                    // note: this is a way to gracefully reenable the keyguard when the call
+                    // ends and the screen is off without always reenabling the keyguard
+                    // each time the screen turns off while in call (and having an occasional ugly
+                    // flicker while turning back on the screen and disabling the keyguard again).
+                    if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
+                            + "keyguard is showing");
+                    doKeyguard();
+                }
+            }
+        }
+    };
+
+
+    /**
+     * When a key is received when the screen is off and the keyguard is showing,
+     * we need to decide whether to actually turn on the screen, and if so, tell
+     * the keyguard to prepare itself and poke the wake lock when it is ready.
+     *
+     * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
+     * Be sure not to take any action that takes a long time; any significant
+     * action should be posted to a handler.
+     *
+     * @param keyCode The keycode of the key that woke the device
+     * @return Whether we poked the wake lock (and turned the screen on)
+     */
+    public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode) {
+        if (DEBUG) Log.d(TAG, "onWakeKeyWhenKeyguardShowing(" + keyCode + ")");
+
+        if (isWakeKeyWhenKeyguardShowing(keyCode)) {
+            // give the keyguard view manager a chance to adjust the state of the
+            // keyguard based on the key that woke the device before poking
+            // the wake lock
+            wakeWhenReadyLocked(keyCode);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isWakeKeyWhenKeyguardShowing(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_CAMERA:
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Callbacks from {@link KeyguardViewManager}.
+     */
+
+    /** {@inheritDoc} */
+    public void pokeWakelock() {
+        pokeWakelock(mKeyboardOpen ?
+                AWAKE_INTERVAL_DEFAULT_KEYBOARD_OPEN_MS : AWAKE_INTERVAL_DEFAULT_MS);
+    }
+
+    /** {@inheritDoc} */
+    public void pokeWakelock(int holdMs) {
+        synchronized (this) {
+            if (DBG_WAKE) Log.d(TAG, "pokeWakelock(" + holdMs + ")");
+            mWakeLock.acquire();
+            mHandler.removeMessages(TIMEOUT);
+            mWakelockSequence++;
+            Message msg = mHandler.obtainMessage(TIMEOUT, mWakelockSequence, 0);
+            mHandler.sendMessageDelayed(msg, holdMs);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see #handleKeyguardDone
+     */
+    public void keyguardDone(boolean authenticated) {
+        keyguardDone(authenticated, true);
+    }
+
+    public void keyguardDone(boolean authenticated, boolean wakeup) {
+        synchronized (this) {
+            EventLog.writeEvent(70000, 2);
+            if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
+            Message msg = mHandler.obtainMessage(KEYGUARD_DONE);
+            msg.arg1 = wakeup ? 1 : 0;
+            mHandler.sendMessage(msg);
+
+            if (authenticated) {
+                mUpdateMonitor.clearFailedAttempts();
+            }
+
+            if (mExitSecureCallback != null) {
+                mExitSecureCallback.onKeyguardExitResult(authenticated);
+                mExitSecureCallback = null;
+
+                if (authenticated) {
+                    // after succesfully exiting securely, no need to reshow
+                    // the keyguard when they've released the lock
+                    mExternallyEnabled = true;
+                    mNeedToReshowWhenReenabled = false;
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see #handleKeyguardDoneDrawing
+     */
+    public void keyguardDoneDrawing() {
+        mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
+    }
+
+    /**
+     * This handler will be associated with the policy thread, which will also
+     * be the UI thread of the keyguard.  Since the apis of the policy, and therefore
+     * this class, can be called by other threads, any action that directly
+     * interacts with the keyguard ui should be posted to this handler, rather
+     * than called directly.
+     */
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case TIMEOUT:
+                    handleTimeout(msg.arg1);
+                    return ;
+                case SHOW:
+                    handleShow();
+                    return ;
+                case HIDE:
+                    handleHide();
+                    return ;
+                case RESET:
+                    handleReset();
+                    return ;
+                case VERIFY_UNLOCK:
+                    handleVerifyUnlock();
+                    return;
+                case NOTIFY_SCREEN_OFF:
+                    handleNotifyScreenOff();
+                    return;
+                case NOTIFY_SCREEN_ON:
+                    handleNotifyScreenOn();
+                    return;
+                case WAKE_WHEN_READY:
+                    handleWakeWhenReady(msg.arg1);
+                    return;
+                case KEYGUARD_DONE:
+                    handleKeyguardDone(msg.arg1 != 0);
+                    return;
+                case KEYGUARD_DONE_DRAWING:
+                    handleKeyguardDoneDrawing();
+                    return;
+                case KEYGUARD_DONE_AUTHENTICATING:
+                    keyguardDone(true);
+                    return;
+                case SET_HIDDEN:
+                    handleSetHidden(msg.arg1 != 0);
+                    break;
+                case KEYGUARD_TIMEOUT:
+                    doKeyguard();
+                    break;
+            }
+        }
+    };
+
+    /**
+     * @see #keyguardDone
+     * @see #KEYGUARD_DONE
+     */
+    private void handleKeyguardDone(boolean wakeup) {
+        if (DEBUG) Log.d(TAG, "handleKeyguardDone");
+        handleHide();
+        if (wakeup) {
+            mPM.userActivity(SystemClock.uptimeMillis(), true);
+        }
+        mWakeLock.release();
+        mContext.sendBroadcast(mUserPresentIntent);
+    }
+
+    /**
+     * @see #keyguardDoneDrawing
+     * @see #KEYGUARD_DONE_DRAWING
+     */
+    private void handleKeyguardDoneDrawing() {
+        synchronized(this) {
+            if (false) Log.d(TAG, "handleKeyguardDoneDrawing");
+            if (mWaitingUntilKeyguardVisible) {
+                if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible");
+                mWaitingUntilKeyguardVisible = false;
+                notifyAll();
+
+                // there will usually be two of these sent, one as a timeout, and one
+                // as a result of the callback, so remove any remaining messages from
+                // the queue
+                mHandler.removeMessages(KEYGUARD_DONE_DRAWING);
+            }
+        }
+    }
+
+    /**
+     * Handles the message sent by {@link #pokeWakelock}
+     * @param seq used to determine if anything has changed since the message
+     *   was sent.
+     * @see #TIMEOUT
+     */
+    private void handleTimeout(int seq) {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleTimeout");
+            if (seq == mWakelockSequence) {
+                mWakeLock.release();
+            }
+        }
+    }
+
+    private void playSounds(boolean locked) {
+        // User feedback for keyguard.
+
+        if (mSuppressNextLockSound) {
+            mSuppressNextLockSound = false;
+            return;
+        }
+
+        final ContentResolver cr = mContext.getContentResolver();
+        if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1)
+        {
+            final String whichSound = locked
+                ? Settings.System.LOCK_SOUND
+                : Settings.System.UNLOCK_SOUND;
+            final String soundPath = Settings.System.getString(cr, whichSound);
+            if (soundPath != null) {
+                final Uri soundUri = Uri.parse("file://" + soundPath);
+                if (soundUri != null) {
+                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+                    if (sfx != null) {
+                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+                        sfx.play();
+                    } else {
+                        Log.d(TAG, "playSounds: failed to load ringtone from uri: " + soundUri);
+                    }
+                } else {
+                    Log.d(TAG, "playSounds: could not parse Uri: " + soundPath);
+                }
+            } else {
+                Log.d(TAG, "playSounds: whichSound = " + whichSound + "; soundPath was null");
+            }
+        }
+    }        
+
+    /**
+     * Handle message sent by {@link #showLocked}.
+     * @see #SHOW
+     */
+    private void handleShow() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleShow");
+            if (!mSystemReady) return;
+
+            playSounds(true);
+
+            mKeyguardViewManager.show();
+            mShowing = true;
+            adjustUserActivityLocked();
+            adjustStatusBarLocked();
+            try {
+                ActivityManagerNative.getDefault().closeSystemDialogs("lock");
+            } catch (RemoteException e) {
+            }
+            mShowKeyguardWakeLock.release();
+        }
+    }
+
+    /**
+     * Handle message sent by {@link #hideLocked()}
+     * @see #HIDE
+     */
+    private void handleHide() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleHide");
+            if (mWakeAndHandOff.isHeld()) {
+                Log.w(TAG, "attempt to hide the keyguard while waking, ignored");
+                return;
+            }
+
+            // only play "unlock" noises if not on a call (since the incall UI
+            // disables the keyguard)
+            if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
+                playSounds(false);
+            }
+
+            mKeyguardViewManager.hide();
+            mShowing = false;
+            adjustUserActivityLocked();
+            adjustStatusBarLocked();
+        }
+    }
+
+    private void adjustUserActivityLocked() {
+        // disable user activity if we are shown and not hidden
+        if (DEBUG) Log.d(TAG, "adjustUserActivityLocked mShowing: " + mShowing + " mHidden: " + mHidden);
+        boolean enabled = !mShowing || mHidden;
+        mRealPowerManager.enableUserActivity(enabled);
+        if (!enabled && mScreenOn) {
+            // reinstate our short screen timeout policy
+            pokeWakelock();
+        }
+    }
+
+    private void adjustStatusBarLocked() {
+        if (mStatusBarManager == null) {
+            mStatusBarManager = (StatusBarManager)
+                    mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+        }
+        if (mStatusBarManager == null) {
+            Log.w(TAG, "Could not get status bar manager");
+        } else {
+            if (mShowLockIcon) {
+                // Give feedback to user when secure keyguard is active and engaged
+                if (mShowing && isSecure()) {
+                    if (mSecureLockIcon == null) {
+                        mSecureLockIcon = mStatusBarManager.addIcon("secure",
+                            com.android.internal.R.drawable.stat_sys_secure, 0);
+                    }
+                } else {
+                    if (mSecureLockIcon != null) {
+                        mStatusBarManager.removeIcon(mSecureLockIcon);
+                        mSecureLockIcon = null;
+                    }
+                }
+            }
+
+            // if the keyguard is shown, allow the status bar to open
+            // only if the keyguard is insecure and is covered by another window
+            boolean enable = !mShowing || (mHidden && !isSecure());
+            mStatusBarManager.disable(enable ?
+                         StatusBarManager.DISABLE_NONE :
+                         StatusBarManager.DISABLE_EXPAND);
+        }
+    }
+
+    /**
+     * Handle message sent by {@link #wakeWhenReadyLocked(int)}
+     * @param keyCode The key that woke the device.
+     * @see #WAKE_WHEN_READY
+     */
+    private void handleWakeWhenReady(int keyCode) {
+        synchronized (KeyguardViewMediator.this) {
+            if (DBG_WAKE) Log.d(TAG, "handleWakeWhenReady(" + keyCode + ")");
+
+            // this should result in a call to 'poke wakelock' which will set a timeout
+            // on releasing the wakelock
+            if (!mKeyguardViewManager.wakeWhenReadyTq(keyCode)) {
+                // poke wakelock ourselves if keyguard is no longer active
+                Log.w(TAG, "mKeyguardViewManager.wakeWhenReadyTq did not poke wake lock, so poke it ourselves");
+                pokeWakelock();
+            }
+
+            /**
+             * Now that the keyguard is ready and has poked the wake lock, we can
+             * release the handoff wakelock
+             */
+            mWakeAndHandOff.release();
+
+            if (!mWakeLock.isHeld()) {
+                Log.w(TAG, "mWakeLock not held in mKeyguardViewManager.wakeWhenReadyTq");
+            }
+        }
+    }
+
+    /**
+     * Handle message sent by {@link #resetStateLocked()}
+     * @see #RESET
+     */
+    private void handleReset() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleReset");
+            mKeyguardViewManager.reset();
+        }
+    }
+
+    /**
+     * Handle message sent by {@link #verifyUnlock}
+     * @see #RESET
+     */
+    private void handleVerifyUnlock() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
+            mKeyguardViewManager.verifyUnlock();
+            mShowing = true;
+        }
+    }
+
+    /**
+     * Handle message sent by {@link #notifyScreenOffLocked()}
+     * @see #NOTIFY_SCREEN_OFF
+     */
+    private void handleNotifyScreenOff() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleNotifyScreenOff");
+            mKeyguardViewManager.onScreenTurnedOff();
+        }
+    }
+
+    /**
+     * Handle message sent by {@link #notifyScreenOnLocked()}
+     * @see #NOTIFY_SCREEN_ON
+     */
+    private void handleNotifyScreenOn() {
+        synchronized (KeyguardViewMediator.this) {
+            if (DEBUG) Log.d(TAG, "handleNotifyScreenOn");
+            mKeyguardViewManager.onScreenTurnedOn();
+        }
+    }
+}
+
+
diff --git a/policy/com/android/internal/policy/impl/KeyguardViewProperties.java b/policy/com/android/internal/policy/impl/KeyguardViewProperties.java
new file mode 100644
index 0000000..bda08eb
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardViewProperties.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.Context;
+
+/**
+ * Defines operations necessary for showing a keyguard, including how to create
+ * it, and various properties that are useful to be able to query independant
+ * of whether the keyguard instance is around or not.
+ */
+public interface KeyguardViewProperties {
+    
+    /**
+     * Create a keyguard view.
+     * @param context the context to use when creating the view.
+     * @param updateMonitor configuration may be based on this.
+     * @param controller for talking back with the containing window.
+     * @return the view.
+     */
+    KeyguardViewBase createKeyguardView(Context context,
+            KeyguardUpdateMonitor updateMonitor,
+            KeyguardWindowController controller);
+
+    /**
+     * Would the keyguard be secure right now?
+     * @return Whether the keyguard is currently secure, meaning it will block
+     *   the user from getting past it until the user enters some sort of PIN.
+     */
+    boolean isSecure();
+
+}
diff --git a/policy/com/android/internal/policy/impl/KeyguardWindowController.java b/policy/com/android/internal/policy/impl/KeyguardWindowController.java
new file mode 100644
index 0000000..4ad48fb
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/KeyguardWindowController.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2009 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.policy.impl;
+
+/**
+ * Interface passed to the keyguard view, for it to call up to control
+ * its containing window.
+ */
+public interface KeyguardWindowController {
+    /**
+     * Control whether the window needs input -- that is if it has
+     * text fields and thus should allow input method interaction.
+     */
+    void setNeedsInput(boolean needsInput);
+}
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
new file mode 100644
index 0000000..c1b14c4
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+import com.android.internal.R;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+
+import java.io.IOException;
+
+/**
+ * The host view for all of the screens of the pattern unlock screen.  There are
+ * two {@link Mode}s of operation, lock and unlock.  This will show the appropriate
+ * screen, and listen for callbacks via
+ * {@link com.android.internal.policy.impl.KeyguardScreenCallback}
+ * from the current screen.
+ *
+ * This view, in turn, communicates back to
+ * {@link com.android.internal.policy.impl.KeyguardViewManager}
+ * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
+ */
+public class LockPatternKeyguardView extends KeyguardViewBase {
+
+    static final boolean DEBUG_CONFIGURATION = false;
+
+    // time after launching EmergencyDialer before the screen goes blank.
+    private static final int EMERGENCY_CALL_TIMEOUT = 10000;
+
+    // intent action for launching emergency dialer activity.
+    static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "LockPatternKeyguardView";
+
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardWindowController mWindowController;
+
+    private View mLockScreen;
+    private View mUnlockScreen;
+
+    private boolean mScreenOn = false;
+    private boolean mEnableFallback = false; // assume no fallback UI until we know better
+
+    /**
+     * The current {@link KeyguardScreen} will use this to communicate back to us.
+     */
+    KeyguardScreenCallback mKeyguardScreenCallback;
+
+
+    private boolean mRequiresSim;
+
+
+    /**
+     * Either a lock screen (an informational keyguard screen), or an unlock
+     * screen (a means for unlocking the device) is shown at any given time.
+     */
+    enum Mode {
+        LockScreen,
+        UnlockScreen
+    }
+
+    /**
+     * The different types screens available for {@link Mode#UnlockScreen}.
+     * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
+     */
+    enum UnlockMode {
+
+        /**
+         * Unlock by drawing a pattern.
+         */
+        Pattern,
+
+        /**
+         * Unlock by entering a sim pin.
+         */
+        SimPin,
+
+        /**
+         * Unlock by entering an account's login and password.
+         */
+        Account,
+
+        /**
+         * Unlock by entering a password or PIN
+         */
+        Password,
+
+        /**
+         * Unknown (uninitialized) value
+         */
+        Unknown
+    }
+
+    /**
+     * The current mode.
+     */
+    private Mode mMode = Mode.LockScreen;
+
+    /**
+     * Keeps track of what mode the current unlock screen is (cached from most recent computation in
+     * {@link #getUnlockMode}).
+     */
+    private UnlockMode mUnlockScreenMode;
+
+    private boolean mForgotPattern;
+
+    /**
+     * If true, it means we are in the process of verifying that the user
+     * can get past the lock screen per {@link #verifyUnlock()}
+     */
+    private boolean mIsVerifyUnlockOnly = false;
+
+
+    /**
+     * Used to lookup the state of the lock pattern
+     */
+    private final LockPatternUtils mLockPatternUtils;
+
+    private UnlockMode mCurrentUnlockMode = UnlockMode.Unknown;
+
+    /**
+     * The current configuration.
+     */
+    private Configuration mConfiguration;
+
+    /**
+     * @return Whether we are stuck on the lock screen because the sim is
+     *   missing.
+     */
+    private boolean stuckOnLockScreenBecauseSimMissing() {
+        return mRequiresSim
+                && (!mUpdateMonitor.isDeviceProvisioned())
+                && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT);
+    }
+
+    /**
+     * @param context Used to inflate, and create views.
+     * @param updateMonitor Knows the state of the world, and passed along to each
+     *   screen so they can use the knowledge, and also register for callbacks
+     *   on dynamic information.
+     * @param lockPatternUtils Used to look up state of lock pattern.
+     */
+    public LockPatternKeyguardView(
+            Context context,
+            KeyguardUpdateMonitor updateMonitor,
+            LockPatternUtils lockPatternUtils,
+            KeyguardWindowController controller) {
+        super(context);
+
+        mConfiguration = context.getResources().getConfiguration();
+        mEnableFallback = false;
+
+        mRequiresSim =
+                TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
+
+        mUpdateMonitor = updateMonitor;
+        mLockPatternUtils = lockPatternUtils;
+        mWindowController = controller;
+
+        mMode = getInitialMode();
+
+        mKeyguardScreenCallback = new KeyguardScreenCallback() {
+
+            public void goToLockScreen() {
+                mForgotPattern = false;
+                if (mIsVerifyUnlockOnly) {
+                    // navigating away from unlock screen during verify mode means
+                    // we are done and the user failed to authenticate.
+                    mIsVerifyUnlockOnly = false;
+                    getCallback().keyguardDone(false);
+                } else {
+                    updateScreen(Mode.LockScreen);
+                }
+            }
+
+            public void goToUnlockScreen() {
+                final IccCard.State simState = mUpdateMonitor.getSimState();
+                if (stuckOnLockScreenBecauseSimMissing()
+                         || (simState == IccCard.State.PUK_REQUIRED)){
+                    // stuck on lock screen when sim missing or puk'd
+                    return;
+                }
+                if (!isSecure()) {
+                    getCallback().keyguardDone(true);
+                } else {
+                    updateScreen(Mode.UnlockScreen);
+                }
+            }
+
+            public void forgotPattern(boolean isForgotten) {
+                if (mEnableFallback) {
+                    mForgotPattern = isForgotten;
+                    updateScreen(Mode.UnlockScreen);
+                }
+            }
+
+            public boolean isSecure() {
+                return LockPatternKeyguardView.this.isSecure();
+            }
+
+            public boolean isVerifyUnlockOnly() {
+                return mIsVerifyUnlockOnly;
+            }
+
+            public void recreateMe(Configuration config) {
+                mConfiguration = config;
+                recreateScreens();
+            }
+
+            public void takeEmergencyCallAction() {
+                pokeWakelock(EMERGENCY_CALL_TIMEOUT);
+                if (TelephonyManager.getDefault().getCallState()
+                        == TelephonyManager.CALL_STATE_OFFHOOK) {
+                    mLockPatternUtils.resumeCall();
+                } else {
+                    Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                    getContext().startActivity(intent);
+                }
+            }
+
+            public void pokeWakelock() {
+                getCallback().pokeWakelock();
+            }
+
+            public void pokeWakelock(int millis) {
+                getCallback().pokeWakelock(millis);
+            }
+
+            public void keyguardDone(boolean authenticated) {
+                getCallback().keyguardDone(authenticated);
+            }
+
+            public void keyguardDoneDrawing() {
+                // irrelevant to keyguard screen, they shouldn't be calling this
+            }
+
+            public void reportFailedUnlockAttempt() {
+                mUpdateMonitor.reportFailedAttempt();
+                final int failedAttempts = mUpdateMonitor.getFailedAttempts();
+                if (DEBUG) Log.d(TAG,
+                    "reportFailedPatternAttempt: #" + failedAttempts +
+                    " (enableFallback=" + mEnableFallback + ")");
+                final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
+                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+                if (usingLockPattern && mEnableFallback && failedAttempts ==
+                        (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+                                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+                    showAlmostAtAccountLoginDialog();
+                } else if (usingLockPattern && mEnableFallback
+                        && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
+                    mLockPatternUtils.setPermanentlyLocked(true);
+                    updateScreen(mMode);
+                } else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)
+                        == 0) {
+                    showTimeoutDialog();
+                }
+                mLockPatternUtils.reportFailedPasswordAttempt();
+            }
+
+            public boolean doesFallbackUnlockScreenExist() {
+                return mEnableFallback;
+            }
+
+            public void reportSuccessfulUnlockAttempt() {
+                mLockPatternUtils.reportSuccessfulPasswordAttempt();
+            }
+        };
+
+        /**
+         * We'll get key events the current screen doesn't use. see
+         * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
+         */
+        setFocusableInTouchMode(true);
+        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+
+        // create both the lock and unlock screen so they are quickly available
+        // when the screen turns on
+        mLockScreen = createLockScreen();
+        addView(mLockScreen);
+        final UnlockMode unlockMode = getUnlockMode();
+        if (DEBUG) Log.d(TAG,
+            "LockPatternKeyguardView ctor: about to createUnlockScreenFor; mEnableFallback="
+            + mEnableFallback);
+        mUnlockScreen = createUnlockScreenFor(unlockMode);
+        mUnlockScreenMode = unlockMode;
+
+        maybeEnableFallback(context);
+
+        addView(mUnlockScreen);
+        updateScreen(mMode);
+    }
+
+    private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
+        private final AccountManager mAccountManager;
+        private final Account[] mAccounts;
+        private int mAccountIndex;
+
+        private AccountAnalyzer(AccountManager accountManager) {
+            mAccountManager = accountManager;
+            mAccounts = accountManager.getAccountsByType("com.google");
+        }
+
+        private void next() {
+            // if we are ready to enable the fallback or if we depleted the list of accounts
+            // then finish and get out
+            if (mEnableFallback || mAccountIndex >= mAccounts.length) {
+                if (mUnlockScreen == null) {
+                    Log.w(TAG, "no unlock screen when trying to enable fallback");
+                } else if (mUnlockScreen instanceof PatternUnlockScreen) {
+                    ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback);
+                }
+                return;
+            }
+
+            // lookup the confirmCredentials intent for the current account
+            mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
+        }
+
+        public void start() {
+            mEnableFallback = false;
+            mAccountIndex = 0;
+            next();
+        }
+
+        public void run(AccountManagerFuture<Bundle> future) {
+            try {
+                Bundle result = future.getResult();
+                if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
+                    mEnableFallback = true;
+                }
+            } catch (OperationCanceledException e) {
+                // just skip the account if we are unable to query it
+            } catch (IOException e) {
+                // just skip the account if we are unable to query it
+            } catch (AuthenticatorException e) {
+                // just skip the account if we are unable to query it
+            } finally {
+                mAccountIndex++;
+                next();
+            }
+        }
+    }
+
+    private void maybeEnableFallback(Context context) {
+        // Ask the account manager if we have an account that can be used as a
+        // fallback in case the user forgets his pattern.
+        AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
+        accountAnalyzer.start();
+    }
+
+
+    // TODO:
+    // This overloaded method was added to workaround a race condition in the framework between
+    // notification for orientation changed, layout() and switching resources.  This code attempts
+    // to avoid drawing the incorrect layout while things are in transition.  The method can just
+    // be removed once the race condition is fixed. See bugs 2262578 and 2292713.
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime());
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
+    public void reset() {
+        mIsVerifyUnlockOnly = false;
+        mForgotPattern = false;
+        updateScreen(getInitialMode());
+    }
+
+    @Override
+    public void onScreenTurnedOff() {
+        mScreenOn = false;
+        mForgotPattern = false;
+        if (mMode == Mode.LockScreen) {
+           ((KeyguardScreen) mLockScreen).onPause();
+        } else {
+            ((KeyguardScreen) mUnlockScreen).onPause();
+        }
+    }
+
+    @Override
+    public void onScreenTurnedOn() {
+        mScreenOn = true;
+        if (mMode == Mode.LockScreen) {
+           ((KeyguardScreen) mLockScreen).onResume();
+        } else {
+            ((KeyguardScreen) mUnlockScreen).onResume();
+        }
+    }
+
+    private void recreateLockScreen() {
+        if (mLockScreen.getVisibility() == View.VISIBLE) {
+            ((KeyguardScreen) mLockScreen).onPause();
+        }
+        ((KeyguardScreen) mLockScreen).cleanUp();
+        removeView(mLockScreen);
+
+        mLockScreen = createLockScreen();
+        mLockScreen.setVisibility(View.INVISIBLE);
+        addView(mLockScreen);
+    }
+
+    private void recreateUnlockScreen() {
+        if (mUnlockScreen.getVisibility() == View.VISIBLE) {
+            ((KeyguardScreen) mUnlockScreen).onPause();
+        }
+        ((KeyguardScreen) mUnlockScreen).cleanUp();
+        removeView(mUnlockScreen);
+
+        final UnlockMode unlockMode = getUnlockMode();
+        mUnlockScreen = createUnlockScreenFor(unlockMode);
+        mUnlockScreen.setVisibility(View.INVISIBLE);
+        mUnlockScreenMode = unlockMode;
+        addView(mUnlockScreen);
+    }
+
+    private void recreateScreens() {
+        recreateLockScreen();
+        recreateUnlockScreen();
+        updateScreen(mMode);
+    }
+
+    @Override
+    public void wakeWhenReadyTq(int keyCode) {
+        if (DEBUG) Log.d(TAG, "onWakeKey");
+        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
+                && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) {
+            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
+            updateScreen(Mode.UnlockScreen);
+            getCallback().pokeWakelock();
+        } else {
+            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
+            getCallback().pokeWakelock();
+        }
+    }
+
+    @Override
+    public void verifyUnlock() {
+        if (!isSecure()) {
+            // non-secure keyguard screens are successfull by default
+            getCallback().keyguardDone(true);
+        } else if (mUnlockScreenMode != UnlockMode.Pattern) {
+            // can only verify unlock when in pattern mode
+            getCallback().keyguardDone(false);
+        } else {
+            // otherwise, go to the unlock screen, see if they can verify it
+            mIsVerifyUnlockOnly = true;
+            updateScreen(Mode.UnlockScreen);
+        }
+    }
+
+    @Override
+    public void cleanUp() {
+        ((KeyguardScreen) mLockScreen).onPause();
+        ((KeyguardScreen) mLockScreen).cleanUp();
+        ((KeyguardScreen) mUnlockScreen).onPause();
+        ((KeyguardScreen) mUnlockScreen).cleanUp();
+    }
+
+    private boolean isSecure() {
+        UnlockMode unlockMode = getUnlockMode();
+        boolean secure = false;
+        switch (unlockMode) {
+            case Pattern:
+                secure = mLockPatternUtils.isLockPatternEnabled();
+                break;
+            case SimPin:
+                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED
+                            || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
+                break;
+            case Account:
+                secure = true;
+                break;
+            case Password:
+                secure = mLockPatternUtils.isLockPasswordEnabled();
+                break;
+            default:
+                throw new IllegalStateException("unknown unlock mode " + unlockMode);
+        }
+        return secure;
+    }
+
+    private void updateScreen(final Mode mode) {
+
+        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
+                + " last mode=" + mMode, new RuntimeException());
+
+        mMode = mode;
+
+        // Re-create the unlock screen if necessary. This is primarily required to properly handle
+        // SIM state changes. This typically happens when this method is called by reset()
+        if (mode == Mode.UnlockScreen && mCurrentUnlockMode != getUnlockMode()) {
+            recreateUnlockScreen();
+        }
+
+        final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
+        final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
+
+        // do this before changing visibility so focus isn't requested before the input
+        // flag is set
+        mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
+
+        if (DEBUG_CONFIGURATION) {
+            Log.v(TAG, "Gone=" + goneScreen);
+            Log.v(TAG, "Visible=" + visibleScreen);
+        }
+
+        if (mScreenOn) {
+            if (goneScreen.getVisibility() == View.VISIBLE) {
+                ((KeyguardScreen) goneScreen).onPause();
+            }
+            if (visibleScreen.getVisibility() != View.VISIBLE) {
+                ((KeyguardScreen) visibleScreen).onResume();
+            }
+        }
+
+        goneScreen.setVisibility(View.GONE);
+        visibleScreen.setVisibility(View.VISIBLE);
+        requestLayout();
+
+        if (!visibleScreen.requestFocus()) {
+            throw new IllegalStateException("keyguard screen must be able to take "
+                    + "focus when shown " + visibleScreen.getClass().getCanonicalName());
+        }
+    }
+
+    View createLockScreen() {
+        return new LockScreen(
+                mContext,
+                mConfiguration,
+                mLockPatternUtils,
+                mUpdateMonitor,
+                mKeyguardScreenCallback);
+    }
+
+    View createUnlockScreenFor(UnlockMode unlockMode) {
+        View unlockView = null;
+        if (unlockMode == UnlockMode.Pattern) {
+            PatternUnlockScreen view = new PatternUnlockScreen(
+                    mContext,
+                    mConfiguration,
+                    mLockPatternUtils,
+                    mUpdateMonitor,
+                    mKeyguardScreenCallback,
+                    mUpdateMonitor.getFailedAttempts());
+            if (DEBUG) Log.d(TAG,
+                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
+            view.setEnableFallback(mEnableFallback);
+            unlockView = view;
+        } else if (unlockMode == UnlockMode.SimPin) {
+            unlockView = new SimUnlockScreen(
+                    mContext,
+                    mConfiguration,
+                    mUpdateMonitor,
+                    mKeyguardScreenCallback,
+                    mLockPatternUtils);
+        } else if (unlockMode == UnlockMode.Account) {
+            try {
+                unlockView = new AccountUnlockScreen(
+                        mContext,
+                        mConfiguration,
+                        mKeyguardScreenCallback,
+                        mLockPatternUtils);
+            } catch (IllegalStateException e) {
+                Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
+                      + " (IAccountsService isn't available)");
+                // TODO: Need a more general way to provide a
+                // platform-specific fallback UI here.
+                // For now, if we can't display the account login
+                // unlock UI, just bring back the regular "Pattern" unlock mode.
+
+                // (We do this by simply returning a regular UnlockScreen
+                // here.  This means that the user will still see the
+                // regular pattern unlock UI, regardless of the value of
+                // mUnlockScreenMode or whether or not we're in the
+                // "permanently locked" state.)
+                unlockView = createUnlockScreenFor(UnlockMode.Pattern);
+            }
+        } else if (unlockMode == UnlockMode.Password) {
+            unlockView = new PasswordUnlockScreen(
+                    mContext,
+                    mConfiguration,
+                    mLockPatternUtils,
+                    mUpdateMonitor,
+                    mKeyguardScreenCallback);
+        } else {
+            throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
+        }
+        mCurrentUnlockMode = unlockMode;
+        return unlockView;
+    }
+
+    /**
+     * Given the current state of things, what should be the initial mode of
+     * the lock screen (lock or unlock).
+     */
+    private Mode getInitialMode() {
+        final IccCard.State simState = mUpdateMonitor.getSimState();
+        if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) {
+            return Mode.LockScreen;
+        } else {
+            // Show LockScreen first for any screen other than Pattern unlock.
+            final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
+                    == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+            if (isSecure() && usingLockPattern) {
+                return Mode.UnlockScreen;
+            } else {
+                return Mode.LockScreen;
+            }
+        }
+    }
+
+    /**
+     * Given the current state of things, what should the unlock screen be?
+     */
+    private UnlockMode getUnlockMode() {
+        final IccCard.State simState = mUpdateMonitor.getSimState();
+        UnlockMode currentMode;
+        if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) {
+            currentMode = UnlockMode.SimPin;
+        } else {
+            final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
+            switch (mode) {
+                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
+                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
+                    currentMode = UnlockMode.Password;
+                    break;
+                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
+                    // "forgot pattern" button is only available in the pattern mode...
+                    if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) {
+                        currentMode = UnlockMode.Account;
+                    } else {
+                        currentMode = UnlockMode.Pattern;
+                    }
+                    break;
+                default:
+                   throw new IllegalStateException("Unknown unlock mode:" + mode);
+            }
+        }
+        return currentMode;
+    }
+
+    private void showTimeoutDialog() {
+        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+        String message = mContext.getString(
+                R.string.lockscreen_too_many_failed_attempts_dialog_message,
+                mUpdateMonitor.getFailedAttempts(),
+                timeoutInSeconds);
+        final AlertDialog dialog = new AlertDialog.Builder(mContext)
+                .setTitle(null)
+                .setMessage(message)
+                .setNeutralButton(R.string.ok, null)
+                .create();
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        if (!mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_slowBlur)) {
+            dialog.getWindow().setFlags(
+                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+        }
+        dialog.show();
+    }
+
+    private void showAlmostAtAccountLoginDialog() {
+        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
+        String message = mContext.getString(
+                R.string.lockscreen_failed_attempts_almost_glogin,
+                LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
+                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT,
+                LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT,
+                timeoutInSeconds);
+        final AlertDialog dialog = new AlertDialog.Builder(mContext)
+                .setTitle(null)
+                .setMessage(message)
+                .setNeutralButton(R.string.ok, null)
+                .create();
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        if (!mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_slowBlur)) {
+            dialog.getWindow().setFlags(
+                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+        }
+        dialog.show();
+    }
+
+    /**
+     * Used to put wallpaper on the background of the lock screen.  Centers it
+     * Horizontally and pins the bottom (assuming that the lock screen is aligned
+     * with the bottom, so the wallpaper should extend above the top into the
+     * status bar).
+     */
+    static private class FastBitmapDrawable extends Drawable {
+        private Bitmap mBitmap;
+        private int mOpacity;
+
+        private FastBitmapDrawable(Bitmap bitmap) {
+            mBitmap = bitmap;
+            mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            canvas.drawBitmap(
+                    mBitmap,
+                    (getBounds().width() - mBitmap.getWidth()) / 2,
+                    (getBounds().height() - mBitmap.getHeight()),
+                    null);
+        }
+
+        @Override
+        public int getOpacity() {
+            return mOpacity;
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            return mBitmap.getWidth();
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return mBitmap.getHeight();
+        }
+
+        @Override
+        public int getMinimumWidth() {
+            return mBitmap.getWidth();
+        }
+
+        @Override
+        public int getMinimumHeight() {
+            return mBitmap.getHeight();
+        }
+    }
+}
+
diff --git a/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java b/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
new file mode 100644
index 0000000..ed5a058
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/LockPatternKeyguardViewProperties.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import android.content.Context;
+import com.android.internal.telephony.IccCard;
+
+/**
+ * Knows how to create a lock pattern keyguard view, and answer questions about
+ * it (even if it hasn't been created, per the interface specs).
+ */
+public class LockPatternKeyguardViewProperties implements KeyguardViewProperties {
+
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+
+    /**
+     * @param lockPatternUtils Used to know whether the pattern enabled, and passed
+     *   onto the keygaurd view when it is created.
+     * @param updateMonitor Used to know whether the sim pin is enabled, and passed
+     *   onto the keyguard view when it is created.
+     */
+    public LockPatternKeyguardViewProperties(LockPatternUtils lockPatternUtils,
+            KeyguardUpdateMonitor updateMonitor) {
+        mLockPatternUtils = lockPatternUtils;
+        mUpdateMonitor = updateMonitor;
+    }
+
+    public KeyguardViewBase createKeyguardView(Context context,
+            KeyguardUpdateMonitor updateMonitor,
+            KeyguardWindowController controller) {
+        return new LockPatternKeyguardView(context, updateMonitor,
+                mLockPatternUtils, controller);
+    }
+
+    public boolean isSecure() {
+        return mLockPatternUtils.isSecure() || isSimPinSecure();
+    }
+
+    private boolean isSimPinSecure() {
+        final IccCard.State simState = mUpdateMonitor.getSimState();
+        return (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED
+            || simState == IccCard.State.ABSENT);
+    }
+
+}
diff --git a/policy/com/android/internal/policy/impl/LockScreen.java b/policy/com/android/internal/policy/impl/LockScreen.java
new file mode 100644
index 0000000..baed9eb
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/LockScreen.java
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import com.android.internal.R;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.SlidingTab;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.ColorStateList;
+import android.text.format.DateFormat;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.*;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.media.AudioManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.provider.Settings;
+
+import java.util.Date;
+import java.io.File;
+
+/**
+ * The screen within {@link LockPatternKeyguardView} that shows general
+ * information about the device depending on its state, and how to get
+ * past it, as applicable.
+ */
+class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback,
+        KeyguardUpdateMonitor.SimStateCallback, SlidingTab.OnTriggerListener {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "LockScreen";
+    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
+
+    private Status mStatus = Status.Normal;
+
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardScreenCallback mCallback;
+
+    private TextView mCarrier;
+    private SlidingTab mSelector;
+    private TextView mTime;
+    private TextView mDate;
+    private TextView mStatus1;
+    private TextView mStatus2;
+    private TextView mScreenLocked;
+    private Button mEmergencyCallButton;
+
+    // current configuration state of keyboard and display
+    private int mKeyboardHidden;
+    private int mCreationOrientation;
+
+    // are we showing battery information?
+    private boolean mShowingBatteryInfo = false;
+
+    // last known plugged in state
+    private boolean mPluggedIn = false;
+
+    // last known battery level
+    private int mBatteryLevel = 100;
+
+    private String mNextAlarm = null;
+    private Drawable mAlarmIcon = null;
+    private String mCharging = null;
+    private Drawable mChargingIcon = null;
+
+    private boolean mSilentMode;
+    private AudioManager mAudioManager;
+    private String mDateFormatString;
+    private java.text.DateFormat mTimeFormat;
+    private boolean mEnableMenuKeyInLockScreen;
+
+    /**
+     * The status of this lock screen.
+     */
+    enum Status {
+        /**
+         * Normal case (sim card present, it's not locked)
+         */
+        Normal(true),
+
+        /**
+         * The sim card is 'network locked'.
+         */
+        NetworkLocked(true),
+
+        /**
+         * The sim card is missing.
+         */
+        SimMissing(false),
+
+        /**
+         * The sim card is missing, and this is the device isn't provisioned, so we don't let
+         * them get past the screen.
+         */
+        SimMissingLocked(false),
+
+        /**
+         * The sim card is PUK locked, meaning they've entered the wrong sim unlock code too many
+         * times.
+         */
+        SimPukLocked(false),
+
+        /**
+         * The sim card is locked.
+         */
+        SimLocked(true);
+
+        private final boolean mShowStatusLines;
+
+        Status(boolean mShowStatusLines) {
+            this.mShowStatusLines = mShowStatusLines;
+        }
+
+        /**
+         * @return Whether the status lines (battery level and / or next alarm) are shown while
+         *         in this state.  Mostly dictated by whether this is room for them.
+         */
+        public boolean showStatusLines() {
+            return mShowStatusLines;
+        }
+    }
+
+    /**
+     * In general, we enable unlocking the insecure key guard with the menu key. However, there are
+     * some cases where we wish to disable it, notably when the menu button placement or technology
+     * is prone to false positives.
+     *
+     * @return true if the menu key should be enabled
+     */
+    private boolean shouldEnableMenuKey() {
+        final Resources res = getResources();
+        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
+        final boolean isMonkey = SystemProperties.getBoolean("ro.monkey", false);
+        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
+        return !configDisabled || isMonkey || fileOverride;
+    }
+
+    /**
+     * @param context Used to setup the view.
+     * @param configuration The current configuration. Used to use when selecting layout, etc.
+     * @param lockPatternUtils Used to know the state of the lock pattern settings.
+     * @param updateMonitor Used to register for updates on various keyguard related
+     *    state, and query the initial state at setup.
+     * @param callback Used to communicate back to the host keyguard view.
+     */
+    LockScreen(Context context, Configuration configuration, LockPatternUtils lockPatternUtils,
+            KeyguardUpdateMonitor updateMonitor,
+            KeyguardScreenCallback callback) {
+        super(context);
+        mLockPatternUtils = lockPatternUtils;
+        mUpdateMonitor = updateMonitor;
+        mCallback = callback;
+
+        mEnableMenuKeyInLockScreen = shouldEnableMenuKey();
+
+        mCreationOrientation = configuration.orientation;
+
+        mKeyboardHidden = configuration.hardKeyboardHidden;
+
+        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
+            Log.v(TAG, "***** CREATING LOCK SCREEN", new RuntimeException());
+            Log.v(TAG, "Cur orient=" + mCreationOrientation
+                    + " res orient=" + context.getResources().getConfiguration().orientation);
+        }
+        
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        if (DBG) Log.v(TAG, "Creation orientation = " + mCreationOrientation);
+        if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
+            inflater.inflate(R.layout.keyguard_screen_tab_unlock, this, true);
+        } else {
+            inflater.inflate(R.layout.keyguard_screen_tab_unlock_land, this, true);
+        }
+
+        mCarrier = (TextView) findViewById(R.id.carrier);
+        // Required for Marquee to work
+        mCarrier.setSelected(true);
+        mCarrier.setTextColor(0xffffffff);
+
+        mDate = (TextView) findViewById(R.id.date);
+        mStatus1 = (TextView) findViewById(R.id.status1);
+        mStatus2 = (TextView) findViewById(R.id.status2);
+
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
+        mEmergencyCallButton.setText(R.string.lockscreen_emergency_call);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        mScreenLocked = (TextView) findViewById(R.id.screenLocked);
+        mSelector = (SlidingTab) findViewById(R.id.tab_selector);
+        mSelector.setHoldAfterTrigger(true, false);
+        mSelector.setLeftHintText(R.string.lockscreen_unlock_label);
+        mEmergencyCallButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                mCallback.takeEmergencyCallAction();
+            }
+        });
+
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+
+        updateMonitor.registerInfoCallback(this);
+        updateMonitor.registerSimStateCallback(this);
+
+        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+        mSilentMode = isSilentMode();
+
+        mSelector.setLeftTabResources(
+                R.drawable.ic_jog_dial_unlock,
+                R.drawable.jog_tab_target_green,
+                R.drawable.jog_tab_bar_left_unlock,
+                R.drawable.jog_tab_left_unlock);
+
+        updateRightTabResources();
+
+        mSelector.setOnTriggerListener(this);
+
+        resetStatusInfo(updateMonitor);
+    }
+
+    private boolean isSilentMode() {
+        return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+    }
+
+    private void updateRightTabResources() {
+        boolean vibe = mSilentMode
+            && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
+
+        mSelector.setRightTabResources(
+                mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on
+                                     : R.drawable.ic_jog_dial_sound_off )
+                            : R.drawable.ic_jog_dial_sound_on,
+                mSilentMode ? R.drawable.jog_tab_target_yellow
+                            : R.drawable.jog_tab_target_gray,
+                mSilentMode ? R.drawable.jog_tab_bar_right_sound_on
+                            : R.drawable.jog_tab_bar_right_sound_off,
+                mSilentMode ? R.drawable.jog_tab_right_sound_on
+                            : R.drawable.jog_tab_right_sound_off);
+    }
+
+    private void resetStatusInfo(KeyguardUpdateMonitor updateMonitor) {
+        mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo();
+        mPluggedIn = updateMonitor.isDevicePluggedIn();
+        mBatteryLevel = updateMonitor.getBatteryLevel();
+
+        mStatus = getCurrentStatus(updateMonitor.getSimState());
+        updateLayout(mStatus);
+
+        refreshBatteryStringAndIcon();
+        refreshAlarmDisplay();
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+
+        mTimeFormat = DateFormat.getTimeFormat(getContext());
+        mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
+        refreshTimeAndDateDisplay();
+        updateStatusLines();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKeyInLockScreen) {
+            mCallback.goToUnlockScreen();
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public void onTrigger(View v, int whichHandle) {
+        if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) {
+            mCallback.goToUnlockScreen();
+        } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
+            // toggle silent mode
+            mSilentMode = !mSilentMode;
+            if (mSilentMode) {
+                final boolean vibe = (Settings.System.getInt(
+                    getContext().getContentResolver(),
+                    Settings.System.VIBRATE_IN_SILENT, 1) == 1);
+
+                mAudioManager.setRingerMode(vibe
+                    ? AudioManager.RINGER_MODE_VIBRATE
+                    : AudioManager.RINGER_MODE_SILENT);
+            } else {
+                mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+            }
+
+            updateRightTabResources();
+
+            String message = mSilentMode ?
+                    getContext().getString(R.string.global_action_silent_mode_on_status) :
+                    getContext().getString(R.string.global_action_silent_mode_off_status);
+
+            final int toastIcon = mSilentMode
+                ? R.drawable.ic_lock_ringer_off
+                : R.drawable.ic_lock_ringer_on;
+
+            final int toastColor = mSilentMode
+                ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff)
+                : getContext().getResources().getColor(R.color.keyguard_text_color_soundon);
+            toastMessage(mScreenLocked, message, toastColor, toastIcon);
+            mCallback.pokeWakelock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void onGrabbedStateChange(View v, int grabbedState) {
+        if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) {
+            mSilentMode = isSilentMode();
+            mSelector.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label
+                    : R.string.lockscreen_sound_off_label);
+        }
+        mCallback.pokeWakelock();
+    }
+
+    /**
+     * Displays a message in a text view and then restores the previous text.
+     * @param textView The text view.
+     * @param text The text.
+     * @param color The color to apply to the text, or 0 if the existing color should be used.
+     * @param iconResourceId The left hand icon.
+     */
+    private void toastMessage(final TextView textView, final String text, final int color, final int iconResourceId) {
+        if (DBG) android.util.Log.d("LockScreen", "toastMessage(text=" + text +", color=" + color + ")");
+
+        if (mPendingR1 != null) {
+            textView.removeCallbacks(mPendingR1);
+            mPendingR1 = null;
+        }
+        if (mPendingR2 != null) {
+            mPendingR2.run(); // fire immediately, restoring non-toasted appearance
+            textView.removeCallbacks(mPendingR2);
+            mPendingR2 = null;
+        }
+
+        final String oldText = textView.getText().toString();
+        final ColorStateList oldColors = textView.getTextColors();
+
+        mPendingR1 = new Runnable() {
+            public void run() {
+                textView.setText(text);
+                if (color != 0) {
+                    textView.setTextColor(color);
+                }
+                textView.setCompoundDrawablesWithIntrinsicBounds(iconResourceId, 0, 0, 0);
+            }
+        };
+
+        textView.postDelayed(mPendingR1, 0);
+        mPendingR2 = new Runnable() {
+            public void run() {
+                textView.setText(oldText);
+                textView.setTextColor(oldColors);
+                textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+            }
+        };
+        textView.postDelayed(mPendingR2, 3500);
+    }
+    private Runnable mPendingR1;
+    private Runnable mPendingR2;
+
+    private void refreshAlarmDisplay() {
+        mNextAlarm = mLockPatternUtils.getNextAlarm();
+        if (mNextAlarm != null) {
+            mAlarmIcon = getContext().getResources().getDrawable(R.drawable.ic_lock_idle_alarm);
+        }
+        updateStatusLines();
+    }
+
+    /** {@inheritDoc} */
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn,
+            int batteryLevel) {
+        if (DBG) Log.d(TAG, "onRefreshBatteryInfo(" + showBatteryInfo + ", " + pluggedIn + ")");
+        mShowingBatteryInfo = showBatteryInfo;
+        mPluggedIn = pluggedIn;
+        mBatteryLevel = batteryLevel;
+
+        refreshBatteryStringAndIcon();
+        updateStatusLines();
+    }
+
+    private void refreshBatteryStringAndIcon() {
+        if (!mShowingBatteryInfo) {
+            mCharging = null;
+            return;
+        }
+
+        if (mChargingIcon == null) {
+            mChargingIcon =
+                    getContext().getResources().getDrawable(R.drawable.ic_lock_idle_charging);
+        }
+
+        if (mPluggedIn) {
+            if (mBatteryLevel >= 100) {
+                mCharging = getContext().getString(R.string.lockscreen_charged);
+            } else {
+                mCharging = getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel);
+            }
+        } else {
+            mCharging = getContext().getString(R.string.lockscreen_low_battery);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void onTimeChanged() {
+        refreshTimeAndDateDisplay();
+    }
+
+    private void refreshTimeAndDateDisplay() {
+        mDate.setText(DateFormat.format(mDateFormatString, new Date()));
+    }
+
+    private void updateStatusLines() {
+        if (!mStatus.showStatusLines()
+                || (mCharging == null && mNextAlarm == null)) {
+            mStatus1.setVisibility(View.INVISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+        } else if (mCharging != null && mNextAlarm == null) {
+            // charging only
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+
+            mStatus1.setText(mCharging);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
+        } else if (mNextAlarm != null && mCharging == null) {
+            // next alarm only
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.INVISIBLE);
+
+            mStatus1.setText(mNextAlarm);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
+        } else if (mCharging != null && mNextAlarm != null) {
+            // both charging and next alarm
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.VISIBLE);
+
+            mStatus1.setText(mCharging);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(mChargingIcon, null, null, null);
+            mStatus2.setText(mNextAlarm);
+            mStatus2.setCompoundDrawablesWithIntrinsicBounds(mAlarmIcon, null, null, null);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+        if (DBG) Log.d(TAG, "onRefreshCarrierInfo(" + plmn + ", " + spn + ")");
+        updateLayout(mStatus);
+    }
+
+    /**
+     * Determine the current status of the lock screen given the sim state and other stuff.
+     */
+    private Status getCurrentStatus(IccCard.State simState) {
+        boolean missingAndNotProvisioned = (!mUpdateMonitor.isDeviceProvisioned()
+                && simState == IccCard.State.ABSENT);
+        if (missingAndNotProvisioned) {
+            return Status.SimMissingLocked;
+        }
+
+        switch (simState) {
+            case ABSENT:
+                return Status.SimMissing;
+            case NETWORK_LOCKED:
+                return Status.SimMissingLocked;
+            case NOT_READY:
+                return Status.SimMissing;
+            case PIN_REQUIRED:
+                return Status.SimLocked;
+            case PUK_REQUIRED:
+                return Status.SimPukLocked;
+            case READY:
+                return Status.Normal;
+            case UNKNOWN:
+                return Status.SimMissing;
+        }
+        return Status.SimMissing;
+    }
+
+    /**
+     * Update the layout to match the current status.
+     */
+    private void updateLayout(Status status) {
+        // The emergency call button appears where the carrier would
+        // ordinarily be shown, so if one is VISIBLE the other must be
+        // INVISIBLE.
+        switch (status) {
+            case Normal:
+                // text
+                mCarrier.setText(
+                        getCarrierString(
+                                mUpdateMonitor.getTelephonyPlmn(),
+                                mUpdateMonitor.getTelephonySpn()));
+//                mScreenLocked.setText(R.string.lockscreen_screen_locked);
+
+                // layout
+                mScreenLocked.setVisibility(View.VISIBLE);
+                mSelector.setVisibility(View.VISIBLE);
+                mEmergencyCallButton.setVisibility(View.GONE);
+                break;
+            case NetworkLocked:
+                // The carrier string shows both sim card status (i.e. No Sim Card) and
+                // carrier's name and/or "Emergency Calls Only" status
+                mCarrier.setText(
+                        getCarrierString(
+                                mUpdateMonitor.getTelephonyPlmn(),
+                                getContext().getText(R.string.lockscreen_network_locked_message)));
+                mScreenLocked.setText(R.string.lockscreen_instructions_when_pattern_disabled);
+
+                // layout
+                mScreenLocked.setVisibility(View.VISIBLE);
+                mSelector.setVisibility(View.VISIBLE);
+                mEmergencyCallButton.setVisibility(View.GONE);
+                break;
+            case SimMissing:
+                // text
+                mCarrier.setText("");
+                mScreenLocked.setText(
+                        getCarrierString(
+                                mUpdateMonitor.getTelephonyPlmn(),
+                                getContext().getText(R.string.lockscreen_missing_sim_message_short)));
+                // previously shown here: lockscreen_instructions_when_pattern_disabled
+
+                // layout
+                mScreenLocked.setVisibility(View.VISIBLE);
+                mSelector.setVisibility(View.VISIBLE);
+                mEmergencyCallButton.setVisibility(View.VISIBLE);
+                break;
+            case SimMissingLocked:
+                // text
+                mCarrier.setText("");
+                mScreenLocked.setText(
+                        getCarrierString(
+                                mUpdateMonitor.getTelephonyPlmn(),
+                                getContext().getText(R.string.lockscreen_missing_sim_message_short)));
+                // previously shown here: lockscreen_missing_sim_instructions
+
+                // layout
+                mScreenLocked.setVisibility(View.VISIBLE);
+                mSelector.setVisibility(View.GONE);
+                mEmergencyCallButton.setVisibility(View.VISIBLE);
+                break;
+            case SimLocked:
+                // text
+                mCarrier.setText(
+                        getCarrierString(
+                                mUpdateMonitor.getTelephonyPlmn(),
+                                getContext().getText(R.string.lockscreen_sim_locked_message)));
+
+                // layout
+                mScreenLocked.setVisibility(View.INVISIBLE);
+                mSelector.setVisibility(View.VISIBLE);
+                mEmergencyCallButton.setVisibility(View.GONE);
+                break;
+            case SimPukLocked:
+                // text
+                mCarrier.setText("");
+                mScreenLocked.setText(
+                        getCarrierString(
+                                mUpdateMonitor.getTelephonyPlmn(),
+                                getContext().getText(R.string.lockscreen_sim_puk_locked_message)));
+                // previously shown here: lockscreen_sim_puk_locked_instructions);
+
+                // layout
+                mScreenLocked.setVisibility(View.VISIBLE);
+                mSelector.setVisibility(View.GONE);
+                mEmergencyCallButton.setVisibility(View.VISIBLE);
+                break;
+        }
+    }
+
+    static CharSequence getCarrierString(CharSequence telephonyPlmn, CharSequence telephonySpn) {
+        if (telephonyPlmn != null && telephonySpn == null) {
+            return telephonyPlmn;
+        } else if (telephonyPlmn != null && telephonySpn != null) {
+            return telephonyPlmn + "|" + telephonySpn;
+        } else if (telephonyPlmn == null && telephonySpn != null) {
+            return telephonySpn;
+        } else {
+            return "";
+        }
+    }
+
+    public void onSimStateChanged(IccCard.State simState) {
+        if (DBG) Log.d(TAG, "onSimStateChanged(" + simState + ")");
+        mStatus = getCurrentStatus(simState);
+        updateLayout(mStatus);
+        updateStatusLines();
+    }
+
+    void updateConfiguration() {
+        Configuration newConfig = getResources().getConfiguration();
+        if (newConfig.orientation != mCreationOrientation) {
+            mCallback.recreateMe(newConfig);
+        } else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
+            mKeyboardHidden = newConfig.hardKeyboardHidden;
+            final boolean isKeyboardOpen = mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
+            if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
+                mCallback.goToUnlockScreen();
+            }
+        }
+    }
+    
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
+            Log.v(TAG, "***** LOCK ATTACHED TO WINDOW");
+            Log.v(TAG, "Cur orient=" + mCreationOrientation
+                    + ", new config=" + getResources().getConfiguration());
+        }
+        updateConfiguration();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
+            Log.w(TAG, "***** LOCK CONFIG CHANGING", new RuntimeException());
+            Log.v(TAG, "Cur orient=" + mCreationOrientation
+                    + ", new config=" + newConfig);
+        }
+        updateConfiguration();
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        resetStatusInfo(mUpdateMonitor);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        mUpdateMonitor.removeCallback(this);
+    }
+
+    /** {@inheritDoc} */
+    public void onRingerModeChanged(int state) {
+        boolean silent = AudioManager.RINGER_MODE_NORMAL != state;
+        if (silent != mSilentMode) {
+            mSilentMode = silent;
+            updateRightTabResources();
+        }
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
new file mode 100644
index 0000000..a0e4f93
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PasswordUnlockScreen.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import com.android.internal.policy.impl.PatternUnlockScreen.FooterMode;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.PasswordEntryKeyboardView;
+
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.text.method.DigitsKeyListener;
+import android.text.method.TextKeyListener;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.internal.R;
+import com.android.internal.widget.PasswordEntryKeyboardHelper;
+
+/**
+ * Displays a dialer-like interface or alphanumeric (latin-1) key entry for the user to enter
+ * an unlock password
+ */
+public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen,
+        View.OnClickListener, KeyguardUpdateMonitor.InfoCallback, OnEditorActionListener {
+
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardScreenCallback mCallback;
+
+    private EditText mPasswordEntry;
+    private Button mEmergencyCallButton;
+    private LockPatternUtils mLockPatternUtils;
+    private PasswordEntryKeyboardView mKeyboardView;
+    private PasswordEntryKeyboardHelper mKeyboardHelper;
+
+    private int mCreationOrientation;
+    private int mKeyboardHidden;
+    private CountDownTimer mCountdownTimer;
+    private TextView mTitle;
+
+    // To avoid accidental lockout due to events while the device in in the pocket, ignore
+    // any passwords with length less than or equal to this length.
+    private static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
+
+    public PasswordUnlockScreen(Context context, Configuration configuration,
+            LockPatternUtils lockPatternUtils, KeyguardUpdateMonitor updateMonitor,
+            KeyguardScreenCallback callback) {
+        super(context);
+
+        mKeyboardHidden = configuration.hardKeyboardHidden;
+        mCreationOrientation = configuration.orientation;
+        mUpdateMonitor = updateMonitor;
+        mCallback = callback;
+        mLockPatternUtils = lockPatternUtils;
+
+        LayoutInflater layoutInflater = LayoutInflater.from(context);
+        if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
+            layoutInflater.inflate(R.layout.keyguard_screen_password_portrait, this, true);
+        } else {
+            layoutInflater.inflate(R.layout.keyguard_screen_password_landscape, this, true);
+        }
+
+        final int quality = lockPatternUtils.getKeyguardStoredPasswordQuality();
+        final boolean isAlpha = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == quality
+                || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == quality;
+
+        mKeyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
+        mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
+        mPasswordEntry.setOnEditorActionListener(this);
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
+        mEmergencyCallButton.setOnClickListener(this);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        mTitle = (TextView) findViewById(R.id.enter_password_label);
+
+        mKeyboardHelper = new PasswordEntryKeyboardHelper(context, mKeyboardView, this);
+        mKeyboardHelper.setKeyboardMode(isAlpha ? PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
+                : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
+
+        mKeyboardView.setVisibility(mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+                ? View.INVISIBLE : View.VISIBLE);
+        mPasswordEntry.requestFocus();
+
+        // This allows keyboards with overlapping qwerty/numeric keys to choose just the
+        // numeric keys.
+        if (isAlpha) {
+            mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+        } else {
+            mPasswordEntry.setKeyListener(DigitsKeyListener.getInstance());
+        }
+
+        mKeyboardHelper.setVibratePattern(mLockPatternUtils.isTactileFeedbackEnabled() ?
+                com.android.internal.R.array.config_virtualKeyVibePattern : 0);
+    }
+
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        // send focus to the password field
+        return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        // start fresh
+        mPasswordEntry.setText("");
+        mPasswordEntry.requestFocus();
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+        if (deadline != 0) {
+            handleAttemptLockout(deadline);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        mUpdateMonitor.removeCallback(this);
+    }
+
+    public void onClick(View v) {
+        if (v == mEmergencyCallButton) {
+            mCallback.takeEmergencyCallAction();
+        }
+        mCallback.pokeWakelock();
+    }
+
+    private void verifyPasswordAndUnlock() {
+        String entry = mPasswordEntry.getText().toString();
+        if (mLockPatternUtils.checkPassword(entry)) {
+            mCallback.keyguardDone(true);
+            mCallback.reportSuccessfulUnlockAttempt();
+        } else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
+            // to avoid accidental lockout, only count attempts that are long enough to be a
+            // real password. This may require some tweaking.
+            mCallback.reportFailedUnlockAttempt();
+            if (0 == (mUpdateMonitor.getFailedAttempts()
+                    % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+                long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+                handleAttemptLockout(deadline);
+            }
+        }
+        mPasswordEntry.setText("");
+    }
+
+    // Prevent user from using the PIN/Password entry until scheduled deadline.
+    private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+        mPasswordEntry.setEnabled(false);
+        mKeyboardView.setEnabled(false);
+        long elapsedRealtime = SystemClock.elapsedRealtime();
+        mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+            @Override
+            public void onTick(long millisUntilFinished) {
+                int secondsRemaining = (int) (millisUntilFinished / 1000);
+                String instructions = getContext().getString(
+                        R.string.lockscreen_too_many_failed_attempts_countdown,
+                        secondsRemaining);
+                mTitle.setText(instructions);
+            }
+
+            @Override
+            public void onFinish() {
+                mPasswordEntry.setEnabled(true);
+                mTitle.setText(R.string.keyguard_password_enter_password_code);
+                mKeyboardView.setEnabled(true);
+            }
+        }.start();
+    }
+
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        mCallback.pokeWakelock();
+        return false;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (getResources().getConfiguration().orientation != mCreationOrientation) {
+            mCallback.recreateMe(getResources().getConfiguration());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (newConfig.orientation != mCreationOrientation) {
+            mCallback.recreateMe(newConfig);
+        }
+    }
+
+    public void onKeyboardChange(boolean isKeyboardOpen) {
+        // Don't show the soft keyboard when the real keyboard is open
+        mKeyboardView.setVisibility(isKeyboardOpen ? View.INVISIBLE : View.VISIBLE);
+    }
+
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        // Check if this was the result of hitting the enter key
+        if (actionId == EditorInfo.IME_NULL) {
+            verifyPasswordAndUnlock();
+            return true;
+        }
+        return false;
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+    }
+
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+
+    }
+
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+
+    }
+
+    public void onRingerModeChanged(int state) {
+
+    }
+
+    public void onTimeChanged() {
+
+    }
+
+}
diff --git a/policy/com/android/internal/policy/impl/PatternUnlockScreen.java b/policy/com/android/internal/policy/impl/PatternUnlockScreen.java
new file mode 100644
index 0000000..418e243
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PatternUnlockScreen.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.MotionEvent;
+import android.widget.Button;
+import android.widget.TextView;
+import android.text.format.DateFormat;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.R;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockPatternView.Cell;
+
+import java.util.List;
+import java.util.Date;
+
+/**
+ * This is the screen that shows the 9 circle unlock widget and instructs
+ * the user how to unlock their device, or make an emergency call.
+ */
+class PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
+        implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback,
+        KeyguardUpdateMonitor.SimStateCallback {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "UnlockScreen";
+
+    // how long before we clear the wrong pattern
+    private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
+
+    // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
+    private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
+
+    // how long we stay awake after the user hits the first dot.
+    private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
+
+    // how many cells the user has to cross before we poke the wakelock
+    private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
+    private int mFailedPatternAttemptsSinceLastTimeout = 0;
+    private int mTotalFailedPatternAttempts = 0;
+    private CountDownTimer mCountdownTimer = null;
+
+    private final LockPatternUtils mLockPatternUtils;
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardScreenCallback mCallback;
+
+    /**
+     * whether there is a fallback option available when the pattern is forgotten.
+     */
+    private boolean mEnableFallback;
+
+    private String mDateFormatString;
+
+    private TextView mCarrier;
+    private TextView mDate;
+
+    // are we showing battery information?
+    private boolean mShowingBatteryInfo = false;
+
+    // last known plugged in state
+    private boolean mPluggedIn = false;
+
+    // last known battery level
+    private int mBatteryLevel = 100;
+
+    private String mNextAlarm = null;
+
+    private String mInstructions = null;
+    private TextView mStatus1;
+    private TextView mStatusSep;
+    private TextView mStatus2;
+
+
+    private LockPatternView mLockPatternView;
+
+    private ViewGroup mFooterNormal;
+    private ViewGroup mFooterForgotPattern;
+
+    /**
+     * Keeps track of the last time we poked the wake lock during dispatching
+     * of the touch event, initalized to something gauranteed to make us
+     * poke it when the user starts drawing the pattern.
+     * @see #dispatchTouchEvent(android.view.MotionEvent)
+     */
+    private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
+
+    /**
+     * Useful for clearing out the wrong pattern after a delay
+     */
+    private Runnable mCancelPatternRunnable = new Runnable() {
+        public void run() {
+            mLockPatternView.clearPattern();
+        }
+    };
+
+    private Button mForgotPatternButton;
+    private Button mEmergencyAlone;
+    private Button mEmergencyTogether;
+    private int mCreationOrientation;
+
+    enum FooterMode {
+        Normal,
+        ForgotLockPattern,
+        VerifyUnlocked
+    }
+
+    private void updateFooter(FooterMode mode) {
+        switch (mode) {
+            case Normal:
+                mFooterNormal.setVisibility(View.VISIBLE);
+                mFooterForgotPattern.setVisibility(View.GONE);
+                break;
+            case ForgotLockPattern:
+                mFooterNormal.setVisibility(View.GONE);
+                mFooterForgotPattern.setVisibility(View.VISIBLE);
+                mForgotPatternButton.setVisibility(View.VISIBLE);
+                break;
+            case VerifyUnlocked:
+                mFooterNormal.setVisibility(View.GONE);
+                mFooterForgotPattern.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * @param context The context.
+     * @param configuration
+     * @param lockPatternUtils Used to lookup lock pattern settings.
+     * @param updateMonitor Used to lookup state affecting keyguard.
+     * @param callback Used to notify the manager when we're done, etc.
+     * @param totalFailedAttempts The current number of failed attempts.
+     * @param enableFallback True if a backup unlock option is available when the user has forgotten
+     *        their pattern (e.g they have a google account so we can show them the account based
+     *        backup option).
+     */
+    PatternUnlockScreen(Context context,
+                 Configuration configuration, LockPatternUtils lockPatternUtils,
+                 KeyguardUpdateMonitor updateMonitor,
+                 KeyguardScreenCallback callback,
+                 int totalFailedAttempts) {
+        super(context);
+        mLockPatternUtils = lockPatternUtils;
+        mUpdateMonitor = updateMonitor;
+        mCallback = callback;
+        mTotalFailedPatternAttempts = totalFailedAttempts;
+        mFailedPatternAttemptsSinceLastTimeout =
+            totalFailedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
+
+        if (DEBUG) Log.d(TAG,
+            "UnlockScreen() ctor: totalFailedAttempts="
+                 + totalFailedAttempts + ", mFailedPat...="
+                 + mFailedPatternAttemptsSinceLastTimeout
+                 );
+
+        mCreationOrientation = configuration.orientation;
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
+            inflater.inflate(R.layout.keyguard_screen_unlock_portrait, this, true);
+        } else {
+            inflater.inflate(R.layout.keyguard_screen_unlock_landscape, this, true);
+        }
+
+        mCarrier = (TextView) findViewById(R.id.carrier);
+        mDate = (TextView) findViewById(R.id.date);
+
+        mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year);
+        refreshTimeAndDateDisplay();
+
+        mStatus1 = (TextView) findViewById(R.id.status1);
+        mStatusSep = (TextView) findViewById(R.id.statusSep);
+        mStatus2 = (TextView) findViewById(R.id.status2);
+
+        resetStatusInfo();
+
+
+        mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
+
+        mFooterNormal = (ViewGroup) findViewById(R.id.footerNormal);
+        mFooterForgotPattern = (ViewGroup) findViewById(R.id.footerForgotPattern);
+
+        // emergency call buttons
+        final OnClickListener emergencyClick = new OnClickListener() {
+            public void onClick(View v) {
+                mCallback.takeEmergencyCallAction();
+            }
+        };
+
+        mEmergencyAlone = (Button) findViewById(R.id.emergencyCallAlone);
+        mEmergencyAlone.setFocusable(false); // touch only!
+        mEmergencyAlone.setOnClickListener(emergencyClick);
+        mEmergencyTogether = (Button) findViewById(R.id.emergencyCallTogether);
+        mEmergencyTogether.setFocusable(false);
+        mEmergencyTogether.setOnClickListener(emergencyClick);
+        refreshEmergencyButtonText();
+
+        mForgotPatternButton = (Button) findViewById(R.id.forgotPattern);
+        mForgotPatternButton.setText(R.string.lockscreen_forgot_pattern_button_text);
+        mForgotPatternButton.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                mCallback.forgotPattern(true);
+            }
+        });
+
+        // make it so unhandled touch events within the unlock screen go to the
+        // lock pattern view.
+        setDefaultTouchRecepient(mLockPatternView);
+
+        mLockPatternView.setSaveEnabled(false);
+        mLockPatternView.setFocusable(false);
+        mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+
+        // stealth mode will be the same for the life of this screen
+        mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled());
+
+        // vibrate mode will be the same for the life of this screen
+        mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+
+        // assume normal footer mode for now
+        updateFooter(FooterMode.Normal);
+
+        updateMonitor.registerInfoCallback(this);
+        updateMonitor.registerSimStateCallback(this);
+        setFocusableInTouchMode(true);
+
+        // Required to get Marquee to work.
+        mCarrier.setSelected(true);
+        mCarrier.setTextColor(0xffffffff);
+
+        // until we get an update...
+        mCarrier.setText(
+                LockScreen.getCarrierString(
+                        mUpdateMonitor.getTelephonyPlmn(),
+                        mUpdateMonitor.getTelephonySpn()));
+    }
+
+    private void refreshEmergencyButtonText() {
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyAlone);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyTogether);
+    }
+
+    public void setEnableFallback(boolean state) {
+        if (DEBUG) Log.d(TAG, "setEnableFallback(" + state + ")");
+        mEnableFallback = state;
+    }
+
+    private void resetStatusInfo() {
+        mInstructions = null;
+        mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo();
+        mPluggedIn = mUpdateMonitor.isDevicePluggedIn();
+        mBatteryLevel = mUpdateMonitor.getBatteryLevel();
+        mNextAlarm = mLockPatternUtils.getNextAlarm();
+        updateStatusLines();
+    }
+
+    private void updateStatusLines() {
+        if (mInstructions != null) {
+            // instructions only
+            mStatus1.setText(mInstructions);
+            if (TextUtils.isEmpty(mInstructions)) {
+                mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+            } else {
+                mStatus1.setCompoundDrawablesWithIntrinsicBounds(
+                        R.drawable.ic_lock_idle_lock, 0, 0, 0);
+            }
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatusSep.setVisibility(View.GONE);
+            mStatus2.setVisibility(View.GONE);
+        } else if (mShowingBatteryInfo && mNextAlarm == null) {
+            // battery only
+            if (mPluggedIn) {
+              if (mBatteryLevel >= 100) {
+                mStatus1.setText(getContext().getString(R.string.lockscreen_charged));
+              } else {
+                  mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel));
+              }
+            } else {
+                mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery));
+            }
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatusSep.setVisibility(View.GONE);
+            mStatus2.setVisibility(View.GONE);
+
+        } else if (mNextAlarm != null && !mShowingBatteryInfo) {
+            // alarm only
+            mStatus1.setText(mNextAlarm);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatusSep.setVisibility(View.GONE);
+            mStatus2.setVisibility(View.GONE);
+        } else if (mNextAlarm != null && mShowingBatteryInfo) {
+            // both battery and next alarm
+            mStatus1.setText(mNextAlarm);
+            mStatusSep.setText("|");
+            mStatus2.setText(getContext().getString(
+                    R.string.lockscreen_battery_short,
+                    Math.min(100, mBatteryLevel)));
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0);
+            if (mPluggedIn) {
+                mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0);
+            } else {
+                mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+            }
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatusSep.setVisibility(View.VISIBLE);
+            mStatus2.setVisibility(View.VISIBLE);
+        } else {
+            // nothing specific to show; show general instructions
+            mStatus1.setText(R.string.lockscreen_pattern_instructions);
+            mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0);
+
+            mStatus1.setVisibility(View.VISIBLE);
+            mStatusSep.setVisibility(View.GONE);
+            mStatus2.setVisibility(View.GONE);
+        }
+    }
+
+
+    private void refreshTimeAndDateDisplay() {
+        mDate.setText(DateFormat.format(mDateFormatString, new Date()));
+    }
+
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        // as long as the user is entering a pattern (i.e sending a touch
+        // event that was handled by this screen), keep poking the
+        // wake lock so that the screen will stay on.
+        final boolean result = super.dispatchTouchEvent(ev);
+        if (result &&
+                ((SystemClock.elapsedRealtime() - mLastPokeTime)
+                        >  (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
+            mLastPokeTime = SystemClock.elapsedRealtime();
+        }
+        return result;
+    }
+
+
+    // ---------- InfoCallback
+
+    /** {@inheritDoc} */
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+        mShowingBatteryInfo = showBatteryInfo;
+        mPluggedIn = pluggedIn;
+        mBatteryLevel = batteryLevel;
+        updateStatusLines();
+    }
+
+    /** {@inheritDoc} */
+    public void onTimeChanged() {
+        refreshTimeAndDateDisplay();
+    }
+
+    /** {@inheritDoc} */
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+        mCarrier.setText(LockScreen.getCarrierString(plmn, spn));
+    }
+
+    /** {@inheritDoc} */
+    public void onRingerModeChanged(int state) {
+        // not currently used
+    }
+
+    // ---------- SimStateCallback
+
+    /** {@inheritDoc} */
+    public void onSimStateChanged(IccCard.State simState) {
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
+            Log.v(TAG, "***** PATTERN ATTACHED TO WINDOW");
+            Log.v(TAG, "Cur orient=" + mCreationOrientation
+                    + ", new config=" + getResources().getConfiguration());
+        }
+        if (getResources().getConfiguration().orientation != mCreationOrientation) {
+            mCallback.recreateMe(getResources().getConfiguration());
+        }
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
+            Log.v(TAG, "***** PATTERN CONFIGURATION CHANGED");
+            Log.v(TAG, "Cur orient=" + mCreationOrientation
+                    + ", new config=" + getResources().getConfiguration());
+        }
+        if (newConfig.orientation != mCreationOrientation) {
+            mCallback.recreateMe(newConfig);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void onKeyboardChange(boolean isKeyboardOpen) {}
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+        if (mCountdownTimer != null) {
+            mCountdownTimer.cancel();
+            mCountdownTimer = null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        // reset header
+        resetStatusInfo();
+
+        // reset lock pattern
+        mLockPatternView.enableInput();
+        mLockPatternView.setEnabled(true);
+        mLockPatternView.clearPattern();
+
+        // show "forgot pattern?" button if we have an alternate authentication method
+        mForgotPatternButton.setVisibility(mCallback.doesFallbackUnlockScreenExist()
+                ? View.VISIBLE : View.INVISIBLE);
+
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
+        if (deadline != 0) {
+            handleAttemptLockout(deadline);
+        }
+
+        // the footer depends on how many total attempts the user has failed
+        if (mCallback.isVerifyUnlockOnly()) {
+            updateFooter(FooterMode.VerifyUnlocked);
+        } else if (mEnableFallback &&
+                (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+            updateFooter(FooterMode.ForgotLockPattern);
+        } else {
+            updateFooter(FooterMode.Normal);
+        }
+
+        refreshEmergencyButtonText();
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        mUpdateMonitor.removeCallback(this);
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        super.onWindowFocusChanged(hasWindowFocus);
+        if (hasWindowFocus) {
+            // when timeout dialog closes we want to update our state
+            onResume();
+        }
+    }
+
+    private class UnlockPatternListener
+            implements LockPatternView.OnPatternListener {
+
+        public void onPatternStart() {
+            mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+        }
+
+        public void onPatternCleared() {
+        }
+
+        public void onPatternCellAdded(List<Cell> pattern) {
+            // To guard against accidental poking of the wakelock, look for
+            // the user actually trying to draw a pattern of some minimal length.
+            if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+                mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+            } else {
+                // Give just a little extra time if they hit one of the first few dots
+                mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
+            }
+        }
+
+        public void onPatternDetected(List<LockPatternView.Cell> pattern) {
+            if (mLockPatternUtils.checkPattern(pattern)) {
+                mLockPatternView
+                        .setDisplayMode(LockPatternView.DisplayMode.Correct);
+                mInstructions = "";
+                updateStatusLines();
+                mCallback.keyguardDone(true);
+                mCallback.reportSuccessfulUnlockAttempt();
+            } else {
+                if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+                    mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
+                }
+                mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+                if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+                    mTotalFailedPatternAttempts++;
+                    mFailedPatternAttemptsSinceLastTimeout++;
+                    mCallback.reportFailedUnlockAttempt();
+                }
+                if (mFailedPatternAttemptsSinceLastTimeout >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+                    long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
+                    handleAttemptLockout(deadline);
+                } else {
+                    // TODO mUnlockIcon.setVisibility(View.VISIBLE);
+                    mInstructions = getContext().getString(R.string.lockscreen_pattern_wrong);
+                    updateStatusLines();
+                    mLockPatternView.postDelayed(
+                            mCancelPatternRunnable,
+                            PATTERN_CLEAR_TIMEOUT_MS);
+                }
+            }
+        }
+    }
+
+    private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+        mLockPatternView.clearPattern();
+        mLockPatternView.setEnabled(false);
+        long elapsedRealtime = SystemClock.elapsedRealtime();
+        mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
+
+            @Override
+            public void onTick(long millisUntilFinished) {
+                int secondsRemaining = (int) (millisUntilFinished / 1000);
+                mInstructions = getContext().getString(
+                        R.string.lockscreen_too_many_failed_attempts_countdown,
+                        secondsRemaining);
+                updateStatusLines();
+            }
+
+            @Override
+            public void onFinish() {
+                mLockPatternView.setEnabled(true);
+                mInstructions = getContext().getString(R.string.lockscreen_pattern_instructions);
+                updateStatusLines();
+                // TODO mUnlockIcon.setVisibility(View.VISIBLE);
+                mFailedPatternAttemptsSinceLastTimeout = 0;
+                if (mEnableFallback) {
+                    updateFooter(FooterMode.ForgotLockPattern);
+                } else {
+                    updateFooter(FooterMode.Normal);
+                }
+            }
+        }.start();
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        refreshEmergencyButtonText();
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/PhoneLayoutInflater.java b/policy/com/android/internal/policy/impl/PhoneLayoutInflater.java
new file mode 100644
index 0000000..6bf4beb
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PhoneLayoutInflater.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2006 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.policy.impl;
+
+import java.util.Map;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.LayoutInflater;
+
+public class PhoneLayoutInflater extends LayoutInflater {
+    private static final String[] sClassPrefixList = {
+        "android.widget.",
+        "android.webkit."
+    };
+    
+    /**
+     * Instead of instantiating directly, you should retrieve an instance
+     * through {@link Context#getSystemService}
+     * 
+     * @param context The Context in which in which to find resources and other
+     *                application-specific things.
+     * 
+     * @see Context#getSystemService
+     */
+    public PhoneLayoutInflater(Context context) {
+        super(context);
+    }
+    
+    protected PhoneLayoutInflater(LayoutInflater original, Context newContext) {
+        super(original, newContext);
+    }
+    
+    /** Override onCreateView to instantiate names that correspond to the
+        widgets known to the Widget factory. If we don't find a match,
+        call through to our super class.
+    */
+    @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
+        for (String prefix : sClassPrefixList) {
+            try {
+                View view = createView(name, prefix, attrs);
+                if (view != null) {
+                    return view;
+                }
+            } catch (ClassNotFoundException e) {
+                // In this case we want to let the base class take a crack
+                // at it.
+            }
+        }
+
+        return super.onCreateView(name, attrs);
+    }
+    
+    public LayoutInflater cloneInContext(Context newContext) {
+        return new PhoneLayoutInflater(this, newContext);
+    }
+}
+
diff --git a/policy/com/android/internal/policy/impl/PhoneWindow.java b/policy/com/android/internal/policy/impl/PhoneWindow.java
new file mode 100644
index 0000000..5592b6d
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PhoneWindow.java
@@ -0,0 +1,2769 @@
+/*
+ *
+ * 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.policy.impl;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+
+import com.android.internal.view.menu.ContextMenuBuilder;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuDialogHelper;
+import com.android.internal.view.menu.MenuView;
+import com.android.internal.view.menu.SubMenuBuilder;
+
+import android.app.KeyguardManager;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.util.AndroidRuntimeException;
+import android.util.Config;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewManager;
+import android.view.VolumePanel;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * Android-specific Window.
+ * <p>
+ * todo: need to pull the generic functionality out into a base class
+ * in android.widget.
+ */
+public class PhoneWindow extends Window implements MenuBuilder.Callback {
+
+    private final static String TAG = "PhoneWindow";
+
+    private final static boolean SWEEP_OPEN_MENU = false;
+
+    /**
+     * Simple callback used by the context menu and its submenus. The options
+     * menu submenus do not use this (their behavior is more complex).
+     */
+    ContextMenuCallback mContextMenuCallback = new ContextMenuCallback(FEATURE_CONTEXT_MENU);
+
+    // This is the top-level view of the window, containing the window decor.
+    private DecorView mDecor;
+
+    // This is the view in which the window contents are placed. It is either
+    // mDecor itself, or a child of mDecor where the contents go.
+    private ViewGroup mContentParent;
+
+    private boolean mIsFloating;
+
+    private LayoutInflater mLayoutInflater;
+
+    private TextView mTitleView;
+
+    private DrawableFeatureState[] mDrawables;
+
+    private PanelFeatureState[] mPanels;
+
+    /**
+     * The panel that is prepared or opened (the most recent one if there are
+     * multiple panels). Shortcuts will go to this panel. It gets set in
+     * {@link #preparePanel} and cleared in {@link #closePanel}.
+     */
+    private PanelFeatureState mPreparedPanel;
+
+    /**
+     * The keycode that is currently held down (as a modifier) for chording. If
+     * this is 0, there is no key held down.
+     */
+    private int mPanelChordingKey;
+    private boolean mPanelMayLongPress;
+
+    private ImageView mLeftIconView;
+
+    private ImageView mRightIconView;
+
+    private ProgressBar mCircularProgressBar;
+
+    private ProgressBar mHorizontalProgressBar;
+
+    private int mBackgroundResource = 0;
+
+    private Drawable mBackgroundDrawable;
+
+    private int mFrameResource = 0;
+
+    private int mTextColor = 0;
+
+    private CharSequence mTitle = null;
+
+    private int mTitleColor = 0;
+
+    private ContextMenuBuilder mContextMenu;
+    private MenuDialogHelper mContextMenuHelper;
+
+    private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
+    private long mVolumeKeyUpTime;
+
+    private KeyguardManager mKeyguardManager = null;
+    
+    private SearchManager mSearchManager = null;
+
+    private TelephonyManager mTelephonyManager = null;
+    
+    public PhoneWindow(Context context) {
+        super(context);
+        mLayoutInflater = LayoutInflater.from(context);
+    }
+
+    @Override
+    public final void setContainer(Window container) {
+        super.setContainer(container);
+    }
+
+    @Override
+    public boolean requestFeature(int featureId) {
+        if (mContentParent != null) {
+            throw new AndroidRuntimeException("requestFeature() must be called before adding content");
+        }
+        final int features = getFeatures();
+        if ((features != DEFAULT_FEATURES) && (featureId == FEATURE_CUSTOM_TITLE)) {
+
+            /* Another feature is enabled and the user is trying to enable the custom title feature */
+            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
+        }
+        if (((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) && (featureId != FEATURE_CUSTOM_TITLE)) {
+
+            /* Custom title feature is enabled and the user is trying to enable another feature */
+            throw new AndroidRuntimeException("You cannot combine custom titles with other title features");
+        }
+        if (featureId == FEATURE_OPENGL) {
+            getAttributes().memoryType = WindowManager.LayoutParams.MEMORY_TYPE_GPU;
+        }
+        return super.requestFeature(featureId);
+    }
+
+    @Override
+    public void setContentView(int layoutResID) {
+        if (mContentParent == null) {
+            installDecor();
+        } else {
+            mContentParent.removeAllViews();
+        }
+        mLayoutInflater.inflate(layoutResID, mContentParent);
+        final Callback cb = getCallback();
+        if (cb != null) {
+            cb.onContentChanged();
+        }
+    }
+
+    @Override
+    public void setContentView(View view) {
+        setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+    }
+
+    @Override
+    public void setContentView(View view, ViewGroup.LayoutParams params) {
+        if (mContentParent == null) {
+            installDecor();
+        } else {
+            mContentParent.removeAllViews();
+        }
+        mContentParent.addView(view, params);
+        final Callback cb = getCallback();
+        if (cb != null) {
+            cb.onContentChanged();
+        }
+    }
+
+    @Override
+    public void addContentView(View view, ViewGroup.LayoutParams params) {
+        if (mContentParent == null) {
+            installDecor();
+        }
+        mContentParent.addView(view, params);
+        final Callback cb = getCallback();
+        if (cb != null) {
+            cb.onContentChanged();
+        }
+    }
+
+    @Override
+    public View getCurrentFocus() {
+        return mDecor != null ? mDecor.findFocus() : null;
+    }
+
+    @Override
+    public boolean isFloating() {
+        return mIsFloating;
+    }
+
+    /**
+     * Return a LayoutInflater instance that can be used to inflate XML view layout
+     * resources for use in this Window.
+     *
+     * @return LayoutInflater The shared LayoutInflater.
+     */
+    @Override
+    public LayoutInflater getLayoutInflater() {
+        return mLayoutInflater;
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        if (mTitleView != null) {
+            mTitleView.setText(title);
+        }
+        mTitle = title;
+    }
+
+    @Override
+    public void setTitleColor(int textColor) {
+        if (mTitleView != null) {
+            mTitleView.setTextColor(textColor);
+        }
+        mTitleColor = textColor;
+    }
+
+    /**
+     * Prepares the panel to either be opened or chorded. This creates the Menu
+     * instance for the panel and populates it via the Activity callbacks.
+     *
+     * @param st The panel state to prepare.
+     * @param event The event that triggered the preparing of the panel.
+     * @return Whether the panel was prepared. If the panel should not be shown,
+     *         returns false.
+     */
+    public final boolean preparePanel(PanelFeatureState st, KeyEvent event) {
+        // Already prepared (isPrepared will be reset to false later)
+        if (st.isPrepared)
+            return true;
+
+        if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
+            // Another Panel is prepared and possibly open, so close it
+            closePanel(mPreparedPanel, false);
+        }
+
+        final Callback cb = getCallback();
+
+        if (cb != null) {
+            st.createdPanelView = cb.onCreatePanelView(st.featureId);
+        }
+
+        if (st.createdPanelView == null) {
+            // Init the panel state's menu--return false if init failed
+            if (st.menu == null) {
+                if (!initializePanelMenu(st) || (st.menu == null)) {
+                    return false;
+                }
+                // Call callback, and return if it doesn't want to display menu
+                if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
+                    // Ditch the menu created above
+                    st.menu = null;
+
+                    return false;
+                }
+            }
+
+            // Callback and return if the callback does not want to show the menu
+            if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
+                return false;
+            }
+
+            // Set the proper keymap
+            KeyCharacterMap kmap = KeyCharacterMap.load(event != null ? event.getDeviceId() : 0);
+            st.qwertyMode = kmap.getKeyboardType() != KeyCharacterMap.NUMERIC;
+            st.menu.setQwertyMode(st.qwertyMode);
+        }
+
+        // Set other state
+        st.isPrepared = true;
+        st.isHandled = false;
+        mPreparedPanel = st;
+
+        return true;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+        if ((st != null) && (st.menu != null)) {
+            final MenuBuilder menuBuilder = (MenuBuilder) st.menu;
+
+            if (st.isOpen) {
+                // Freeze state
+                final Bundle state = new Bundle();
+                menuBuilder.saveHierarchyState(state);
+
+                // Remove the menu views since they need to be recreated
+                // according to the new configuration
+                clearMenuViews(st);
+
+                // Re-open the same menu
+                reopenMenu(false);
+
+                // Restore state
+                menuBuilder.restoreHierarchyState(state);
+
+            } else {
+                // Clear menu views so on next menu opening, it will use
+                // the proper layout
+                clearMenuViews(st);
+            }
+        }
+
+    }
+
+    private static void clearMenuViews(PanelFeatureState st) {
+
+        // This can be called on config changes, so we should make sure
+        // the views will be reconstructed based on the new orientation, etc.
+
+        // Allow the callback to create a new panel view
+        st.createdPanelView = null;
+
+        // Causes the decor view to be recreated
+        st.refreshDecorView = true;
+
+        ((MenuBuilder) st.menu).clearMenuViews();
+    }
+
+    @Override
+    public final void openPanel(int featureId, KeyEvent event) {
+        openPanel(getPanelState(featureId, true), event);
+    }
+
+    private void openPanel(PanelFeatureState st, KeyEvent event) {
+        // System.out.println("Open panel: isOpen=" + st.isOpen);
+
+        // Already open, return
+        if (st.isOpen) {
+            return;
+        }
+
+        Callback cb = getCallback();
+        if ((cb != null) && (!cb.onMenuOpened(st.featureId, st.menu))) {
+            // Callback doesn't want the menu to open, reset any state
+            closePanel(st, true);
+            return;
+        }
+
+        final WindowManager wm = getWindowManager();
+        if (wm == null) {
+            return;
+        }
+
+        // Prepare panel (should have been done before, but just in case)
+        if (!preparePanel(st, event)) {
+            return;
+        }
+
+        if (st.decorView == null || st.refreshDecorView) {
+            if (st.decorView == null) {
+                // Initialize the panel decor, this will populate st.decorView
+                if (!initializePanelDecor(st) || (st.decorView == null))
+                    return;
+            } else if (st.refreshDecorView && (st.decorView.getChildCount() > 0)) {
+                // Decor needs refreshing, so remove its views
+                st.decorView.removeAllViews();
+            }
+
+            // This will populate st.shownPanelView
+            if (!initializePanelContent(st) || (st.shownPanelView == null)) {
+                return;
+            }
+
+            ViewGroup.LayoutParams lp = st.shownPanelView.getLayoutParams();
+            if (lp == null) {
+                lp = new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+            }
+
+            int backgroundResId;
+            if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
+                // If the contents is fill parent for the width, set the
+                // corresponding background
+                backgroundResId = st.fullBackground;
+            } else {
+                // Otherwise, set the normal panel background
+                backgroundResId = st.background;
+            }
+            st.decorView.setWindowBackground(getContext().getResources().getDrawable(
+                    backgroundResId));
+
+
+            st.decorView.addView(st.shownPanelView, lp);
+
+            /*
+             * Give focus to the view, if it or one of its children does not
+             * already have it.
+             */
+            if (!st.shownPanelView.hasFocus()) {
+                st.shownPanelView.requestFocus();
+            }
+        }
+
+        st.isOpen = true;
+        st.isHandled = false;
+
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                WRAP_CONTENT, WRAP_CONTENT,
+                st.x, st.y, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG,
+                WindowManager.LayoutParams.FLAG_DITHER
+                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                st.decorView.mDefaultOpacity);
+
+        lp.gravity = st.gravity;
+        lp.windowAnimations = st.windowAnimations;
+        
+        wm.addView(st.decorView, lp);
+        // Log.v(TAG, "Adding main menu to window manager.");
+    }
+
+    @Override
+    public final void closePanel(int featureId) {
+        if (featureId == FEATURE_CONTEXT_MENU) {
+            closeContextMenu();
+        } else {
+            closePanel(getPanelState(featureId, true), true);
+        }
+    }
+
+    /**
+     * Closes the given panel.
+     *
+     * @param st The panel to be closed.
+     * @param doCallback Whether to notify the callback that the panel was
+     *            closed. If the panel is in the process of re-opening or
+     *            opening another panel (e.g., menu opening a sub menu), the
+     *            callback should not happen and this variable should be false.
+     *            In addition, this method internally will only perform the
+     *            callback if the panel is open.
+     */
+    public final void closePanel(PanelFeatureState st, boolean doCallback) {
+        // System.out.println("Close panel: isOpen=" + st.isOpen);
+        final ViewManager wm = getWindowManager();
+        if ((wm != null) && st.isOpen) {
+            if (st.decorView != null) {
+                wm.removeView(st.decorView);
+                // Log.v(TAG, "Removing main menu from window manager.");
+            }
+
+            if (doCallback) {
+                callOnPanelClosed(st.featureId, st, null);
+            }
+        }
+        st.isPrepared = false;
+        st.isHandled = false;
+        st.isOpen = false;
+
+        // This view is no longer shown, so null it out
+        st.shownPanelView = null;
+
+        if (st.isInExpandedMode) {
+            // Next time the menu opens, it should not be in expanded mode, so
+            // force a refresh of the decor
+            st.refreshDecorView = true;
+            st.isInExpandedMode = false;
+        }
+
+        if (mPreparedPanel == st) {
+            mPreparedPanel = null;
+            mPanelChordingKey = 0;
+        }
+    }
+
+    @Override
+    public final void togglePanel(int featureId, KeyEvent event) {
+        PanelFeatureState st = getPanelState(featureId, true);
+        if (st.isOpen) {
+            closePanel(st, true);
+        } else {
+            openPanel(st, event);
+        }
+    }
+
+    /**
+     * Called when the panel key is pushed down.
+     * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
+     * @param event The key event.
+     * @return Whether the key was handled.
+     */
+    public final boolean onKeyDownPanel(int featureId, KeyEvent event) {
+        final int keyCode = event.getKeyCode();
+        
+        if (event.getRepeatCount() == 0) {
+            // The panel key was pushed, so set the chording key
+            mPanelChordingKey = keyCode;
+            mPanelMayLongPress = false;
+            
+            PanelFeatureState st = getPanelState(featureId, true);
+            if (!st.isOpen) {
+                if (getContext().getResources().getConfiguration().keyboard
+                        == Configuration.KEYBOARD_NOKEYS) {
+                    mPanelMayLongPress = true;
+                }
+                return preparePanel(st, event);
+            }
+            
+        } else if (mPanelMayLongPress && mPanelChordingKey == keyCode
+                && (event.getFlags()&KeyEvent.FLAG_LONG_PRESS) != 0) {
+            // We have had a long press while in a state where this
+            // should be executed...  do it!
+            mPanelChordingKey = 0;
+            mPanelMayLongPress = false;
+            InputMethodManager imm = (InputMethodManager)
+                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            if (imm != null) {
+                mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
+            }
+            
+        }
+
+        return false;
+    }
+
+    /**
+     * Called when the panel key is released.
+     * @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
+     * @param event The key event.
+     */
+    public final void onKeyUpPanel(int featureId, KeyEvent event) {
+        // The panel key was released, so clear the chording key
+        if (mPanelChordingKey != 0) {
+            mPanelChordingKey = 0;
+            mPanelMayLongPress = false;
+
+            if (event.isCanceled()) {
+                return;
+            }
+            
+            boolean playSoundEffect = false;
+            PanelFeatureState st = getPanelState(featureId, true);
+            if (st.isOpen || st.isHandled) {
+
+                // Play the sound effect if the user closed an open menu (and not if
+                // they just released a menu shortcut)
+                playSoundEffect = st.isOpen;
+
+                // Close menu
+                closePanel(st, true);
+
+            } else if (st.isPrepared) {
+
+                // Write 'menu opened' to event log
+                EventLog.writeEvent(50001, 0);
+
+                // Show menu
+                openPanel(st, event);
+
+                playSoundEffect = true;
+            }
+
+            if (playSoundEffect) {
+                AudioManager audioManager = (AudioManager) getContext().getSystemService(
+                        Context.AUDIO_SERVICE);
+                if (audioManager != null) {
+                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
+                } else {
+                    Log.w(TAG, "Couldn't get audio manager");
+                }
+            }
+        }
+    }
+
+    @Override
+    public final void closeAllPanels() {
+        final ViewManager wm = getWindowManager();
+        if (wm == null) {
+            return;
+        }
+
+        final PanelFeatureState[] panels = mPanels;
+        final int N = panels != null ? panels.length : 0;
+        for (int i = 0; i < N; i++) {
+            final PanelFeatureState panel = panels[i];
+            if (panel != null) {
+                closePanel(panel, true);
+            }
+        }
+
+        closeContextMenu();
+    }
+
+    /**
+     * Closes the context menu. This notifies the menu logic of the close, along
+     * with dismissing it from the UI.
+     */
+    private synchronized void closeContextMenu() {
+        if (mContextMenu != null) {
+            mContextMenu.close();
+            dismissContextMenu();
+        }
+    }
+
+    /**
+     * Dismisses just the context menu UI. To close the context menu, use
+     * {@link #closeContextMenu()}.
+     */
+    private synchronized void dismissContextMenu() {
+        mContextMenu = null;
+
+        if (mContextMenuHelper != null) {
+            mContextMenuHelper.dismiss();
+            mContextMenuHelper = null;
+        }
+    }
+
+    @Override
+    public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
+        return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags);
+    }
+
+    private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
+            int flags) {
+        if (event.isSystem() || (st == null)) {
+            return false;
+        }
+
+        boolean handled = false;
+
+        // Only try to perform menu shortcuts if preparePanel returned true (possible false
+        // return value from application not wanting to show the menu).
+        if ((st.isPrepared || preparePanel(st, event)) && st.menu != null) {
+            // The menu is prepared now, perform the shortcut on it
+            handled = st.menu.performShortcut(keyCode, event, flags);
+        }
+
+        if (handled) {
+            // Mark as handled
+            st.isHandled = true;
+
+            if ((flags & Menu.FLAG_PERFORM_NO_CLOSE) == 0) {
+                closePanel(st, true);
+            }
+        }
+
+        return handled;
+    }
+
+    @Override
+    public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
+
+        PanelFeatureState st = getPanelState(featureId, true);
+        if (!preparePanel(st, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU))) {
+            return false;
+        }
+        if (st.menu == null) {
+            return false;
+        }
+
+        boolean res = st.menu.performIdentifierAction(id, flags);
+
+        closePanel(st, true);
+
+        return res;
+    }
+
+    public PanelFeatureState findMenuPanel(Menu menu) {
+        final PanelFeatureState[] panels = mPanels;
+        final int N = panels != null ? panels.length : 0;
+        for (int i = 0; i < N; i++) {
+            final PanelFeatureState panel = panels[i];
+            if (panel != null && panel.menu == menu) {
+                return panel;
+            }
+        }
+        return null;
+    }
+
+    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+        final Callback cb = getCallback();
+        if (cb != null) {
+            final PanelFeatureState panel = findMenuPanel(menu.getRootMenu());
+            if (panel != null) {
+                return cb.onMenuItemSelected(panel.featureId, item);
+            }
+        }
+        return false;
+    }
+
+    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+        final PanelFeatureState panel = findMenuPanel(menu);
+        if (panel != null) {
+            // Close the panel and only do the callback if the menu is being
+            // closed
+            // completely, not if opening a sub menu
+            closePanel(panel, allMenusAreClosing);
+        }
+    }
+
+    public void onCloseSubMenu(SubMenuBuilder subMenu) {
+        final Menu parentMenu = subMenu.getRootMenu();
+        final PanelFeatureState panel = findMenuPanel(parentMenu);
+
+        // Callback
+        if (panel != null) {
+            callOnPanelClosed(panel.featureId, panel, parentMenu);
+            closePanel(panel, true);
+        }
+    }
+
+    public boolean onSubMenuSelected(final SubMenuBuilder subMenu) {
+        if (!subMenu.hasVisibleItems()) {
+            return true;
+        }
+
+        // The window manager will give us a valid window token
+        new MenuDialogHelper(subMenu).show(null);
+
+        return true;
+    }
+
+    public void onMenuModeChange(MenuBuilder menu) {
+        reopenMenu(true);
+    }
+
+    private void reopenMenu(boolean toggleMenuMode) {
+        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+
+        // Save the future expanded mode state since closePanel will reset it
+        boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode;
+
+        st.refreshDecorView = true;
+        closePanel(st, false);
+
+        // Set the expanded mode state
+        st.isInExpandedMode = newExpandedMode;
+
+        openPanel(st, null);
+    }
+
+    /**
+     * Initializes the menu associated with the given panel feature state. You
+     * must at the very least set PanelFeatureState.menu to the Menu to be
+     * associated with the given panel state. The default implementation creates
+     * a new menu for the panel state.
+     *
+     * @param st The panel whose menu is being initialized.
+     * @return Whether the initialization was successful.
+     */
+    protected boolean initializePanelMenu(final PanelFeatureState st) {
+        final MenuBuilder menu = new MenuBuilder(getContext());
+
+        menu.setCallback(this);
+        st.setMenu(menu);
+
+        return true;
+    }
+
+    /**
+     * Perform initial setup of a panel. This should at the very least set the
+     * style information in the PanelFeatureState and must set
+     * PanelFeatureState.decor to the panel's window decor view.
+     *
+     * @param st The panel being initialized.
+     */
+    protected boolean initializePanelDecor(PanelFeatureState st) {
+        st.decorView = new DecorView(getContext(), st.featureId);
+        st.gravity = Gravity.CENTER | Gravity.BOTTOM;
+        st.setStyle(getContext());
+
+        return true;
+    }
+
+    /**
+     * Initializes the panel associated with the panel feature state. You must
+     * at the very least set PanelFeatureState.panel to the View implementing
+     * its contents. The default implementation gets the panel from the menu.
+     *
+     * @param st The panel state being initialized.
+     * @return Whether the initialization was successful.
+     */
+    protected boolean initializePanelContent(PanelFeatureState st) {
+
+        if (st.createdPanelView != null) {
+            st.shownPanelView = st.createdPanelView;
+            return true;
+        }
+
+        final MenuBuilder menu = (MenuBuilder)st.menu;
+        if (menu == null) {
+            return false;
+        }
+
+        st.shownPanelView = menu.getMenuView((st.isInExpandedMode) ? MenuBuilder.TYPE_EXPANDED
+                : MenuBuilder.TYPE_ICON, st.decorView);
+
+        if (st.shownPanelView != null) {
+            // Use the menu View's default animations if it has any
+            final int defaultAnimations = ((MenuView) st.shownPanelView).getWindowAnimations();
+            if (defaultAnimations != 0) {
+                st.windowAnimations = defaultAnimations;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean performContextMenuIdentifierAction(int id, int flags) {
+        return (mContextMenu != null) ? mContextMenu.performIdentifierAction(id, flags) : false;
+    }
+
+    @Override
+    public final void setBackgroundDrawable(Drawable drawable) {
+        if (drawable != mBackgroundDrawable || mBackgroundResource != 0) {
+            mBackgroundResource = 0;
+            mBackgroundDrawable = drawable;
+            if (mDecor != null) {
+                mDecor.setWindowBackground(drawable);
+            }
+        }
+    }
+
+    @Override
+    public final void setFeatureDrawableResource(int featureId, int resId) {
+        if (resId != 0) {
+            DrawableFeatureState st = getDrawableState(featureId, true);
+            if (st.resid != resId) {
+                st.resid = resId;
+                st.uri = null;
+                st.local = getContext().getResources().getDrawable(resId);
+                updateDrawable(featureId, st, false);
+            }
+        } else {
+            setFeatureDrawable(featureId, null);
+        }
+    }
+
+    @Override
+    public final void setFeatureDrawableUri(int featureId, Uri uri) {
+        if (uri != null) {
+            DrawableFeatureState st = getDrawableState(featureId, true);
+            if (st.uri == null || !st.uri.equals(uri)) {
+                st.resid = 0;
+                st.uri = uri;
+                st.local = loadImageURI(uri);
+                updateDrawable(featureId, st, false);
+            }
+        } else {
+            setFeatureDrawable(featureId, null);
+        }
+    }
+
+    @Override
+    public final void setFeatureDrawable(int featureId, Drawable drawable) {
+        DrawableFeatureState st = getDrawableState(featureId, true);
+        st.resid = 0;
+        st.uri = null;
+        if (st.local != drawable) {
+            st.local = drawable;
+            updateDrawable(featureId, st, false);
+        }
+    }
+
+    @Override
+    public void setFeatureDrawableAlpha(int featureId, int alpha) {
+        DrawableFeatureState st = getDrawableState(featureId, true);
+        if (st.alpha != alpha) {
+            st.alpha = alpha;
+            updateDrawable(featureId, st, false);
+        }
+    }
+
+    protected final void setFeatureDefaultDrawable(int featureId, Drawable drawable) {
+        DrawableFeatureState st = getDrawableState(featureId, true);
+        if (st.def != drawable) {
+            st.def = drawable;
+            updateDrawable(featureId, st, false);
+        }
+    }
+
+    @Override
+    public final void setFeatureInt(int featureId, int value) {
+        // XXX Should do more management (as with drawable features) to
+        // deal with interactions between multiple window policies.
+        updateInt(featureId, value, false);
+    }
+
+    /**
+     * Update the state of a drawable feature. This should be called, for every
+     * drawable feature supported, as part of onActive(), to make sure that the
+     * contents of a containing window is properly updated.
+     *
+     * @see #onActive
+     * @param featureId The desired drawable feature to change.
+     * @param fromActive Always true when called from onActive().
+     */
+    protected final void updateDrawable(int featureId, boolean fromActive) {
+        final DrawableFeatureState st = getDrawableState(featureId, false);
+        if (st != null) {
+            updateDrawable(featureId, st, fromActive);
+        }
+    }
+
+    /**
+     * Called when a Drawable feature changes, for the window to update its
+     * graphics.
+     *
+     * @param featureId The feature being changed.
+     * @param drawable The new Drawable to show, or null if none.
+     * @param alpha The new alpha blending of the Drawable.
+     */
+    protected void onDrawableChanged(int featureId, Drawable drawable, int alpha) {
+        ImageView view;
+        if (featureId == FEATURE_LEFT_ICON) {
+            view = getLeftIconView();
+        } else if (featureId == FEATURE_RIGHT_ICON) {
+            view = getRightIconView();
+        } else {
+            return;
+        }
+
+        if (drawable != null) {
+            drawable.setAlpha(alpha);
+            view.setImageDrawable(drawable);
+            view.setVisibility(View.VISIBLE);
+        } else {
+            view.setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * Called when an int feature changes, for the window to update its
+     * graphics.
+     *
+     * @param featureId The feature being changed.
+     * @param value The new integer value.
+     */
+    protected void onIntChanged(int featureId, int value) {
+        if (featureId == FEATURE_PROGRESS || featureId == FEATURE_INDETERMINATE_PROGRESS) {
+            updateProgressBars(value);
+        } else if (featureId == FEATURE_CUSTOM_TITLE) {
+            FrameLayout titleContainer = (FrameLayout) findViewById(com.android.internal.R.id.title_container);
+            if (titleContainer != null) {
+                mLayoutInflater.inflate(value, titleContainer);
+            }
+        }
+    }
+
+    /**
+     * Updates the progress bars that are shown in the title bar.
+     *
+     * @param value Can be one of {@link Window#PROGRESS_VISIBILITY_ON},
+     *            {@link Window#PROGRESS_VISIBILITY_OFF},
+     *            {@link Window#PROGRESS_INDETERMINATE_ON},
+     *            {@link Window#PROGRESS_INDETERMINATE_OFF}, or a value
+     *            starting at {@link Window#PROGRESS_START} through
+     *            {@link Window#PROGRESS_END} for setting the default
+     *            progress (if {@link Window#PROGRESS_END} is given,
+     *            the progress bar widgets in the title will be hidden after an
+     *            animation), a value between
+     *            {@link Window#PROGRESS_SECONDARY_START} -
+     *            {@link Window#PROGRESS_SECONDARY_END} for the
+     *            secondary progress (if
+     *            {@link Window#PROGRESS_SECONDARY_END} is given, the
+     *            progress bar widgets will still be shown with the secondary
+     *            progress bar will be completely filled in.)
+     */
+    private void updateProgressBars(int value) {
+        ProgressBar circularProgressBar = getCircularProgressBar(true);
+        ProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
+
+        final int features = getLocalFeatures();
+        if (value == PROGRESS_VISIBILITY_ON) {
+            if ((features & (1 << FEATURE_PROGRESS)) != 0) {
+                int level = horizontalProgressBar.getProgress();
+                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
+                        View.VISIBLE : View.INVISIBLE;
+                horizontalProgressBar.setVisibility(visibility);
+            }
+            if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
+                circularProgressBar.setVisibility(View.VISIBLE);
+            }
+        } else if (value == PROGRESS_VISIBILITY_OFF) {
+            if ((features & (1 << FEATURE_PROGRESS)) != 0) {
+                horizontalProgressBar.setVisibility(View.GONE);
+            }
+            if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
+                circularProgressBar.setVisibility(View.GONE);
+            }
+        } else if (value == PROGRESS_INDETERMINATE_ON) {
+            horizontalProgressBar.setIndeterminate(true);
+        } else if (value == PROGRESS_INDETERMINATE_OFF) {
+            horizontalProgressBar.setIndeterminate(false);
+        } else if (PROGRESS_START <= value && value <= PROGRESS_END) {
+            // We want to set the progress value before testing for visibility
+            // so that when the progress bar becomes visible again, it has the
+            // correct level.
+            horizontalProgressBar.setProgress(value - PROGRESS_START);
+
+            if (value < PROGRESS_END) {
+                showProgressBars(horizontalProgressBar, circularProgressBar);
+            } else {
+                hideProgressBars(horizontalProgressBar, circularProgressBar);
+            }
+        } else if (PROGRESS_SECONDARY_START <= value && value <= PROGRESS_SECONDARY_END) {
+            horizontalProgressBar.setSecondaryProgress(value - PROGRESS_SECONDARY_START);
+
+            showProgressBars(horizontalProgressBar, circularProgressBar);
+        }
+
+    }
+
+    private void showProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
+        final int features = getLocalFeatures();
+        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
+                spinnyProgressBar.getVisibility() == View.INVISIBLE) {
+            spinnyProgressBar.setVisibility(View.VISIBLE);
+        }
+        // Only show the progress bars if the primary progress is not complete
+        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
+                horizontalProgressBar.getProgress() < 10000) {
+            horizontalProgressBar.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private void hideProgressBars(ProgressBar horizontalProgressBar, ProgressBar spinnyProgressBar) {
+        final int features = getLocalFeatures();
+        Animation anim = AnimationUtils.loadAnimation(getContext(), com.android.internal.R.anim.fade_out);
+        anim.setDuration(1000);
+        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
+                spinnyProgressBar.getVisibility() == View.VISIBLE) {
+            spinnyProgressBar.startAnimation(anim);
+            spinnyProgressBar.setVisibility(View.INVISIBLE);
+        }
+        if ((features & (1 << FEATURE_PROGRESS)) != 0 &&
+                horizontalProgressBar.getVisibility() == View.VISIBLE) {
+            horizontalProgressBar.startAnimation(anim);
+            horizontalProgressBar.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    /**
+     * Request that key events come to this activity. Use this if your activity
+     * has no views with focus, but the activity still wants a chance to process
+     * key events.
+     */
+    @Override
+    public void takeKeyEvents(boolean get) {
+        mDecor.setFocusable(get);
+    }
+
+    @Override
+    public boolean superDispatchKeyEvent(KeyEvent event) {
+        return mDecor.superDispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean superDispatchTouchEvent(MotionEvent event) {
+        return mDecor.superDispatchTouchEvent(event);
+    }
+
+    @Override
+    public boolean superDispatchTrackballEvent(MotionEvent event) {
+        return mDecor.superDispatchTrackballEvent(event);
+    }
+
+    /**
+     * A key was pressed down and not handled by anything else in the window.
+     *
+     * @see #onKeyUp
+     * @see android.view.KeyEvent
+     */
+    protected boolean onKeyDown(int featureId, int keyCode, KeyEvent event) {
+        final KeyEvent.DispatcherState dispatcher =
+                mDecor != null ? mDecor.getKeyDispatcherState() : null;
+        //Log.i(TAG, "Key down: repeat=" + event.getRepeatCount()
+        //        + " flags=0x" + Integer.toHexString(event.getFlags()));
+        
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN: {
+                AudioManager audioManager = (AudioManager) getContext().getSystemService(
+                        Context.AUDIO_SERVICE);
+                if (audioManager != null) {
+                    /*
+                     * Adjust the volume in on key down since it is more
+                     * responsive to the user.
+                     */
+                    audioManager.adjustSuggestedStreamVolume(
+                            keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                                    ? AudioManager.ADJUST_RAISE
+                                    : AudioManager.ADJUST_LOWER,
+                            mVolumeControlStreamType,
+                            AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
+                }
+                return true;
+            }
+
+
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+                /* Suppress PLAYPAUSE toggle when phone is ringing or in-call
+                 * to avoid music playback */
+                if (mTelephonyManager == null) {
+                    mTelephonyManager = (TelephonyManager) getContext().getSystemService(
+                            Context.TELEPHONY_SERVICE);
+                }
+                if (mTelephonyManager != null &&
+                        mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
+                    return true;  // suppress key event
+                }
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+                getContext().sendOrderedBroadcast(intent, null);
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_CAMERA: {
+                if (getKeyguardManager().inKeyguardRestrictedInputMode()
+                        || dispatcher == null) {
+                    break;
+                }
+                if (event.getRepeatCount() == 0) {
+                    dispatcher.startTracking(event, this);
+                } else if (event.isLongPress() && dispatcher.isTracking(event)) {
+                    dispatcher.performedLongPress(event);
+                    mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                    sendCloseSystemWindows();
+                    // Broadcast an intent that the Camera button was longpressed
+                    Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
+                    intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+                    getContext().sendOrderedBroadcast(intent, null);
+                }
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_MENU: {
+                onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_BACK: {
+                if (event.getRepeatCount() > 0) break;
+                if (featureId < 0) break;
+                // Currently don't do anything with long press.
+                dispatcher.startTracking(event, this);
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_CALL: {
+                if (getKeyguardManager().inKeyguardRestrictedInputMode()
+                        || dispatcher == null) {
+                    break;
+                }
+                if (event.getRepeatCount() == 0) {
+                    dispatcher.startTracking(event, this);
+                } else if (event.isLongPress() && dispatcher.isTracking(event)) {
+                    dispatcher.performedLongPress(event);
+                    mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                    // launch the VoiceDialer
+                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    try {
+                        sendCloseSystemWindows();
+                        getContext().startActivity(intent);
+                    } catch (ActivityNotFoundException e) {
+                        startCallActivity();
+                    }
+                }
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_SEARCH: {
+                if (getKeyguardManager().inKeyguardRestrictedInputMode()
+                        || dispatcher == null) {
+                    break;
+                }
+                if (event.getRepeatCount() == 0) {
+                    dispatcher.startTracking(event, this);
+                } else if (event.isLongPress() && dispatcher.isTracking(event)) {
+                    Configuration config = getContext().getResources().getConfiguration(); 
+                    if (config.keyboard == Configuration.KEYBOARD_NOKEYS
+                            || config.hardKeyboardHidden
+                                    == Configuration.HARDKEYBOARDHIDDEN_YES) {
+                        // launch the search activity
+                        Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
+                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        try {
+                            mDecor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                            sendCloseSystemWindows();
+                            getSearchManager().stopSearch();
+                            getContext().startActivity(intent);
+                            // Only clear this if we successfully start the
+                            // activity; otherwise we will allow the normal short
+                            // press action to be performed.
+                            dispatcher.performedLongPress(event);
+                            return true;
+                        } catch (ActivityNotFoundException e) {
+                            // Ignore
+                        }
+                    }
+                }
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * @return A handle to the keyguard manager.
+     */
+    private KeyguardManager getKeyguardManager() {
+        if (mKeyguardManager == null) {
+            mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+        }
+        return mKeyguardManager;
+    }
+    
+    /**
+     * @return A handle to the search manager.
+     */
+    private SearchManager getSearchManager() {
+        if (mSearchManager == null) {
+            mSearchManager = (SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE);
+        }
+        return mSearchManager;
+    }
+
+    /**
+     * A key was released and not handled by anything else in the window.
+     *
+     * @see #onKeyDown
+     * @see android.view.KeyEvent
+     */
+    protected boolean onKeyUp(int featureId, int keyCode, KeyEvent event) {
+        final KeyEvent.DispatcherState dispatcher =
+                mDecor != null ? mDecor.getKeyDispatcherState() : null;
+        if (dispatcher != null) {
+            dispatcher.handleUpEvent(event);
+        }
+        //Log.i(TAG, "Key up: repeat=" + event.getRepeatCount()
+        //        + " flags=0x" + Integer.toHexString(event.getFlags()));
+        
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN: {
+                AudioManager audioManager = (AudioManager) getContext().getSystemService(
+                        Context.AUDIO_SERVICE);
+                if (audioManager != null) {
+                    /*
+                     * Play a sound. This is done on key up since we don't want the
+                     * sound to play when a user holds down volume down to mute.
+                     */
+                    audioManager.adjustSuggestedStreamVolume(
+                            AudioManager.ADJUST_SAME,
+                            mVolumeControlStreamType,
+                            AudioManager.FLAG_PLAY_SOUND);
+                    mVolumeKeyUpTime = SystemClock.uptimeMillis();
+                }
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_MENU: {
+                onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
+                        event);
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_BACK: {
+                if (featureId < 0) break;
+                if (event.isTracking() && !event.isCanceled()) {
+                    if (featureId == FEATURE_OPTIONS_PANEL) {
+                        PanelFeatureState st = getPanelState(featureId, false);
+                        if (st != null && st.isInExpandedMode) {
+                            // If the user is in an expanded menu and hits back, it
+                            // should go back to the icon menu
+                            reopenMenu(true);
+                            return true;
+                        }
+                    }
+                    closePanel(featureId);
+                    return true;
+                }
+                break;
+            }
+
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+                getContext().sendOrderedBroadcast(intent, null);
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_CAMERA: {
+                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
+                    break;
+                }
+                if (event.isTracking() && !event.isCanceled()) {
+                    // Add short press behavior here if desired
+                }
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_CALL: {
+                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
+                    break;
+                }
+                if (event.isTracking() && !event.isCanceled()) {
+                    startCallActivity();
+                }
+                return true;
+            }
+
+            case KeyEvent.KEYCODE_SEARCH: {
+                /*
+                 * Do this in onKeyUp since the Search key is also used for
+                 * chording quick launch shortcuts.
+                 */
+                if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
+                    break;
+                }
+                if (event.isTracking() && !event.isCanceled()) {
+                    launchDefaultSearch();
+                }
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void startCallActivity() {
+        sendCloseSystemWindows();
+        Intent intent = new Intent(Intent.ACTION_CALL_BUTTON);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getContext().startActivity(intent);
+    }
+
+    @Override
+    protected void onActive() {
+    }
+
+    @Override
+    public final View getDecorView() {
+        if (mDecor == null) {
+            installDecor();
+        }
+        return mDecor;
+    }
+
+    @Override
+    public final View peekDecorView() {
+        return mDecor;
+    }
+
+    static private final String FOCUSED_ID_TAG = "android:focusedViewId";
+    static private final String VIEWS_TAG = "android:views";
+    static private final String PANELS_TAG = "android:Panels";
+
+    /** {@inheritDoc} */
+    @Override
+    public Bundle saveHierarchyState() {
+        Bundle outState = new Bundle();
+        if (mContentParent == null) {
+            return outState;
+        }
+
+        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
+        mContentParent.saveHierarchyState(states);
+        outState.putSparseParcelableArray(VIEWS_TAG, states);
+
+        // save the focused view id
+        View focusedView = mContentParent.findFocus();
+        if (focusedView != null) {
+            if (focusedView.getId() != View.NO_ID) {
+                outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
+            } else {
+                if (Config.LOGD) {
+                    Log.d(TAG, "couldn't save which view has focus because the focused view "
+                            + focusedView + " has no id.");
+                }
+            }
+        }
+
+        // save the panels
+        SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
+        savePanelState(panelStates);
+        if (panelStates.size() > 0) {
+            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
+        }
+
+        return outState;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void restoreHierarchyState(Bundle savedInstanceState) {
+        if (mContentParent == null) {
+            return;
+        }
+
+        SparseArray<Parcelable> savedStates
+                = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
+        if (savedStates != null) {
+            mContentParent.restoreHierarchyState(savedStates);
+        }
+
+        // restore the focused view
+        int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
+        if (focusedViewId != View.NO_ID) {
+            View needsFocus = mContentParent.findViewById(focusedViewId);
+            if (needsFocus != null) {
+                needsFocus.requestFocus();
+            } else {
+                Log.w(TAG,
+                        "Previously focused view reported id " + focusedViewId
+                                + " during save, but can't be found during restore.");
+            }
+        }
+
+        // restore the panels
+        SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
+        if (panelStates != null) {
+            restorePanelState(panelStates);
+        }
+    }
+
+    /**
+     * Invoked when the panels should freeze their state.
+     *
+     * @param icicles Save state into this. This is usually indexed by the
+     *            featureId. This will be given to {@link #restorePanelState} in the
+     *            future.
+     */
+    private void savePanelState(SparseArray<Parcelable> icicles) {
+        PanelFeatureState[] panels = mPanels;
+        if (panels == null) {
+            return;
+        }
+
+        for (int curFeatureId = panels.length - 1; curFeatureId >= 0; curFeatureId--) {
+            if (panels[curFeatureId] != null) {
+                icicles.put(curFeatureId, panels[curFeatureId].onSaveInstanceState());
+            }
+        }
+    }
+
+    /**
+     * Invoked when the panels should thaw their state from a previously frozen state.
+     *
+     * @param icicles The state saved by {@link #savePanelState} that needs to be thawed.
+     */
+    private void restorePanelState(SparseArray<Parcelable> icicles) {
+        PanelFeatureState st;
+        for (int curFeatureId = icicles.size() - 1; curFeatureId >= 0; curFeatureId--) {
+            st = getPanelState(curFeatureId, false /* required */);
+            if (st == null) {
+                // The panel must not have been required, and is currently not around, skip it
+                continue;
+            }
+
+            st.onRestoreInstanceState(icicles.get(curFeatureId));
+        }
+
+        /*
+         * Implementation note: call openPanelsAfterRestore later to actually open the
+         * restored panels.
+         */
+    }
+
+    /**
+     * Opens the panels that have had their state restored. This should be
+     * called sometime after {@link #restorePanelState} when it is safe to add
+     * to the window manager.
+     */
+    private void openPanelsAfterRestore() {
+        PanelFeatureState[] panels = mPanels;
+
+        if (panels == null) {
+            return;
+        }
+
+        PanelFeatureState st;
+        for (int i = panels.length - 1; i >= 0; i--) {
+            st = panels[i];
+            // We restore the panel if it was last open; we skip it if it
+            // now is open, to avoid a race condition if the user immediately
+            // opens it when we are resuming.
+            if ((st != null) && !st.isOpen && st.wasLastOpen) {
+                st.isInExpandedMode = st.wasLastExpanded;
+                openPanel(st, null);
+            }
+        }
+    }
+
+    private final class DecorView extends FrameLayout {
+        /* package */int mDefaultOpacity = PixelFormat.OPAQUE;
+
+        /** The feature ID of the panel, or -1 if this is the application's DecorView */
+        private final int mFeatureId;
+
+        private final Rect mDrawingBounds = new Rect();
+
+        private final Rect mBackgroundPadding = new Rect();
+
+        private final Rect mFramePadding = new Rect();
+
+        private final Rect mFrameOffsets = new Rect();
+
+        private boolean mChanging;
+
+        private Drawable mMenuBackground;
+        private boolean mWatchingForMenu;
+        private int mDownY;
+
+        public DecorView(Context context, int featureId) {
+            super(context);
+            mFeatureId = featureId;
+        }
+
+        @Override
+        public boolean dispatchKeyEvent(KeyEvent event) {
+            final int keyCode = event.getKeyCode();
+            final boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
+
+            /*
+             * If the user hits another key within the play sound delay, then
+             * cancel the sound
+             */
+            if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP
+                    && mVolumeKeyUpTime + VolumePanel.PLAY_SOUND_DELAY
+                            > SystemClock.uptimeMillis()) {
+                /*
+                 * The user has hit another key during the delay (e.g., 300ms)
+                 * since the last volume key up, so cancel any sounds.
+                 */
+                AudioManager audioManager = (AudioManager) getContext().getSystemService(
+                        Context.AUDIO_SERVICE);
+                if (audioManager != null) {
+                    audioManager.adjustSuggestedStreamVolume(AudioManager.ADJUST_SAME,
+                            mVolumeControlStreamType, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
+                }
+            }
+
+            if (isDown && (event.getRepeatCount() == 0)) {
+                // First handle chording of panel key: if a panel key is held
+                // but not released, try to execute a shortcut in it.
+                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
+                    // Perform the shortcut (mPreparedPanel can be null since
+                    // global shortcuts (such as search) don't rely on a
+                    // prepared panel or menu).
+                    boolean handled = performPanelShortcut(mPreparedPanel, keyCode, event,
+                            Menu.FLAG_PERFORM_NO_CLOSE);
+
+                    if (!handled) {
+                        /*
+                         * If not handled, then pass it to the view hierarchy
+                         * and anyone else that may be interested.
+                         */
+                        handled = dispatchKeyShortcutEvent(event);
+
+                        if (handled && mPreparedPanel != null) {
+                            mPreparedPanel.isHandled = true;
+                        }
+                    }
+
+                    if (handled) {
+                        return true;
+                    }
+                }
+
+                // If a panel is open, perform a shortcut on it without the
+                // chorded panel key
+                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
+                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
+                        return true;
+                    }
+                }
+            }
+
+            final Callback cb = getCallback();
+            final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
+                    : super.dispatchKeyEvent(event);
+            if (handled) {
+                return true;
+            }
+            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
+                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            final Callback cb = getCallback();
+            return cb != null && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super
+                    .dispatchTouchEvent(ev);
+        }
+
+        @Override
+        public boolean dispatchTrackballEvent(MotionEvent ev) {
+            final Callback cb = getCallback();
+            return cb != null && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev) : super
+                    .dispatchTrackballEvent(ev);
+        }
+
+        public boolean superDispatchKeyEvent(KeyEvent event) {
+            return super.dispatchKeyEvent(event);
+        }
+
+        public boolean superDispatchTouchEvent(MotionEvent event) {
+            return super.dispatchTouchEvent(event);
+        }
+
+        public boolean superDispatchTrackballEvent(MotionEvent event) {
+            return super.dispatchTrackballEvent(event);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            return onInterceptTouchEvent(event);
+        }
+
+        private boolean isOutOfBounds(int x, int y) {
+            return x < -5 || y < -5 || x > (getWidth() + 5)
+                    || y > (getHeight() + 5);
+        }
+
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent event) {
+            int action = event.getAction();
+            if (mFeatureId >= 0) {
+                if (action == MotionEvent.ACTION_DOWN) {
+                    int x = (int)event.getX();
+                    int y = (int)event.getY();
+                    if (isOutOfBounds(x, y)) {
+                        closePanel(mFeatureId);
+                        return true;
+                    }
+                }
+            }
+
+            if (!SWEEP_OPEN_MENU) {
+                return false;
+            }
+
+            if (mFeatureId >= 0) {
+                if (action == MotionEvent.ACTION_DOWN) {
+                    Log.i(TAG, "Watchiing!");
+                    mWatchingForMenu = true;
+                    mDownY = (int) event.getY();
+                    return false;
+                }
+
+                if (!mWatchingForMenu) {
+                    return false;
+                }
+
+                int y = (int)event.getY();
+                if (action == MotionEvent.ACTION_MOVE) {
+                    if (y > (mDownY+30)) {
+                        Log.i(TAG, "Closing!");
+                        closePanel(mFeatureId);
+                        mWatchingForMenu = false;
+                        return true;
+                    }
+                } else if (action == MotionEvent.ACTION_UP) {
+                    mWatchingForMenu = false;
+                }
+
+                return false;
+            }
+
+            //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
+            //        + " (in " + getHeight() + ")");
+
+            if (action == MotionEvent.ACTION_DOWN) {
+                int y = (int)event.getY();
+                if (y >= (getHeight()-5) && !hasChildren()) {
+                    Log.i(TAG, "Watchiing!");
+                    mWatchingForMenu = true;
+                }
+                return false;
+            }
+
+            if (!mWatchingForMenu) {
+                return false;
+            }
+
+            int y = (int)event.getY();
+            if (action == MotionEvent.ACTION_MOVE) {
+                if (y < (getHeight()-30)) {
+                    Log.i(TAG, "Opening!");
+                    openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
+                            KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
+                    mWatchingForMenu = false;
+                    return true;
+                }
+            } else if (action == MotionEvent.ACTION_UP) {
+                mWatchingForMenu = false;
+            }
+
+            return false;
+        }
+
+        @Override
+        public void sendAccessibilityEvent(int eventType) {
+            if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
+                return;
+            }
+ 
+            // if we are showing a feature that should be announced and one child
+            // make this child the event source since this is the feature itself
+            // otherwise the callback will take over and announce its client
+            if ((mFeatureId == FEATURE_OPTIONS_PANEL ||
+                    mFeatureId == FEATURE_CONTEXT_MENU ||
+                    mFeatureId == FEATURE_PROGRESS ||
+                    mFeatureId == FEATURE_INDETERMINATE_PROGRESS)
+                    && getChildCount() == 1) {
+                getChildAt(0).sendAccessibilityEvent(eventType);
+            } else {
+                super.sendAccessibilityEvent(eventType);
+            }
+        }
+
+        @Override
+        public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+            final Callback cb = getCallback();
+            if (cb != null) {
+                if (cb.dispatchPopulateAccessibilityEvent(event)) {
+                    return true;
+                }
+            }
+            return super.dispatchPopulateAccessibilityEvent(event);
+        }
+
+        @Override
+        protected boolean setFrame(int l, int t, int r, int b) {
+            boolean changed = super.setFrame(l, t, r, b);
+            if (changed) {
+                final Rect drawingBounds = mDrawingBounds;
+                getDrawingRect(drawingBounds);
+
+                Drawable fg = getForeground();
+                if (fg != null) {
+                    final Rect frameOffsets = mFrameOffsets;
+                    drawingBounds.left += frameOffsets.left;
+                    drawingBounds.top += frameOffsets.top;
+                    drawingBounds.right -= frameOffsets.right;
+                    drawingBounds.bottom -= frameOffsets.bottom;
+                    fg.setBounds(drawingBounds);
+                    final Rect framePadding = mFramePadding;
+                    drawingBounds.left += framePadding.left - frameOffsets.left;
+                    drawingBounds.top += framePadding.top - frameOffsets.top;
+                    drawingBounds.right -= framePadding.right - frameOffsets.right;
+                    drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
+                }
+
+                Drawable bg = getBackground();
+                if (bg != null) {
+                    bg.setBounds(drawingBounds);
+                }
+
+                if (SWEEP_OPEN_MENU) {
+                    if (mMenuBackground == null && mFeatureId < 0
+                            && getAttributes().height
+                            == WindowManager.LayoutParams.MATCH_PARENT) {
+                        mMenuBackground = getContext().getResources().getDrawable(
+                                com.android.internal.R.drawable.menu_background);
+                    }
+                    if (mMenuBackground != null) {
+                        mMenuBackground.setBounds(drawingBounds.left,
+                                drawingBounds.bottom-6, drawingBounds.right,
+                                drawingBounds.bottom+20);
+                    }
+                }
+            }
+            return changed;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            super.draw(canvas);
+
+            if (mMenuBackground != null) {
+                mMenuBackground.draw(canvas);
+            }
+        }
+
+
+        @Override
+        public boolean showContextMenuForChild(View originalView) {
+            // Reuse the context menu builder
+            if (mContextMenu == null) {
+                mContextMenu = new ContextMenuBuilder(getContext());
+                mContextMenu.setCallback(mContextMenuCallback);
+            } else {
+                mContextMenu.clearAll();
+            }
+
+            mContextMenuHelper = mContextMenu.show(originalView, originalView.getWindowToken());
+            return mContextMenuHelper != null;
+        }
+
+        public void startChanging() {
+            mChanging = true;
+        }
+
+        public void finishChanging() {
+            mChanging = false;
+            drawableChanged();
+        }
+
+        public void setWindowBackground(Drawable drawable) {
+            if (getBackground() != drawable) {
+                setBackgroundDrawable(drawable);
+                if (drawable != null) {
+                    drawable.getPadding(mBackgroundPadding);
+                } else {
+                    mBackgroundPadding.setEmpty();
+                }
+                drawableChanged();
+            }
+        }
+
+        public void setWindowFrame(Drawable drawable) {
+            if (getForeground() != drawable) {
+                setForeground(drawable);
+                if (drawable != null) {
+                    drawable.getPadding(mFramePadding);
+                } else {
+                    mFramePadding.setEmpty();
+                }
+                drawableChanged();
+            }
+        }
+
+        @Override
+        protected boolean fitSystemWindows(Rect insets) {
+            mFrameOffsets.set(insets);
+            if (getForeground() != null) {
+                drawableChanged();
+            }
+            return super.fitSystemWindows(insets);
+        }
+
+        private void drawableChanged() {
+            if (mChanging) {
+                return;
+            }
+
+            setPadding(mFramePadding.left + mBackgroundPadding.left, mFramePadding.top
+                    + mBackgroundPadding.top, mFramePadding.right + mBackgroundPadding.right,
+                    mFramePadding.bottom + mBackgroundPadding.bottom);
+            requestLayout();
+            invalidate();
+
+            int opacity = PixelFormat.OPAQUE;
+
+            // Note: if there is no background, we will assume opaque. The
+            // common case seems to be that an application sets there to be
+            // no background so it can draw everything itself. For that,
+            // we would like to assume OPAQUE and let the app force it to
+            // the slower TRANSLUCENT mode if that is really what it wants.
+            Drawable bg = getBackground();
+            Drawable fg = getForeground();
+            if (bg != null) {
+                if (fg == null) {
+                    opacity = bg.getOpacity();
+                } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
+                        && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
+                    // If the frame padding is zero, then we can be opaque
+                    // if either the frame -or- the background is opaque.
+                    int fop = fg.getOpacity();
+                    int bop = bg.getOpacity();
+                    if (Config.LOGV)
+                        Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
+                    if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
+                        opacity = PixelFormat.OPAQUE;
+                    } else if (fop == PixelFormat.UNKNOWN) {
+                        opacity = bop;
+                    } else if (bop == PixelFormat.UNKNOWN) {
+                        opacity = fop;
+                    } else {
+                        opacity = Drawable.resolveOpacity(fop, bop);
+                    }
+                } else {
+                    // For now we have to assume translucent if there is a
+                    // frame with padding... there is no way to tell if the
+                    // frame and background together will draw all pixels.
+                    if (Config.LOGV)
+                        Log.v(TAG, "Padding: " + mFramePadding);
+                    opacity = PixelFormat.TRANSLUCENT;
+                }
+            }
+
+            if (Config.LOGV)
+                Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
+            if (Config.LOGV)
+                Log.v(TAG, "Selected default opacity: " + opacity);
+
+            mDefaultOpacity = opacity;
+            if (mFeatureId < 0) {
+                setDefaultWindowFormat(opacity);
+            }
+        }
+
+        @Override
+        public void onWindowFocusChanged(boolean hasWindowFocus) {
+            super.onWindowFocusChanged(hasWindowFocus);
+
+            mPanelMayLongPress = false;
+
+            // If the user is chording a menu shortcut, release the chord since
+            // this window lost focus
+            if (!hasWindowFocus && mPanelChordingKey != 0) {
+                closePanel(FEATURE_OPTIONS_PANEL);
+            }
+
+            final Callback cb = getCallback();
+            if (cb != null && mFeatureId < 0) {
+                cb.onWindowFocusChanged(hasWindowFocus);
+            }
+        }
+
+        @Override
+        protected void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            
+            final Callback cb = getCallback();
+            if (cb != null && mFeatureId < 0) {
+                cb.onAttachedToWindow();
+            }
+
+            if (mFeatureId == -1) {
+                /*
+                 * The main window has been attached, try to restore any panels
+                 * that may have been open before. This is called in cases where
+                 * an activity is being killed for configuration change and the
+                 * menu was open. When the activity is recreated, the menu
+                 * should be shown again.
+                 */
+                openPanelsAfterRestore();
+            }
+        }
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            
+            final Callback cb = getCallback();
+            if (cb != null && mFeatureId < 0) {
+                cb.onDetachedFromWindow();
+            }
+        }
+        
+        @Override
+        public void onCloseSystemDialogs(String reason) {
+            if (mFeatureId >= 0) {
+                closeAllPanels();
+            }
+        }
+    }
+
+    protected DecorView generateDecor() {
+        return new DecorView(getContext(), -1);
+    }
+
+    protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
+            int drawableAttr, int alphaAttr) {
+        Drawable d = attrs.getDrawable(drawableAttr);
+        if (d != null) {
+            requestFeature(featureId);
+            setFeatureDefaultDrawable(featureId, d);
+        }
+        if ((getFeatures() & (1 << featureId)) != 0) {
+            int alpha = attrs.getInt(alphaAttr, -1);
+            if (alpha >= 0) {
+                setFeatureDrawableAlpha(featureId, alpha);
+            }
+        }
+    }
+
+    protected ViewGroup generateLayout(DecorView decor) {
+        // Apply data from current theme.
+
+        TypedArray a = getWindowStyle();
+
+        if (false) {
+            System.out.println("From style:");
+            String s = "Attrs:";
+            for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
+                s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
+                        + a.getString(i);
+            }
+            System.out.println(s);
+        }
+
+        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
+        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
+                & (~getForcedWindowFlags());
+        if (mIsFloating) {
+            setLayout(WRAP_CONTENT, WRAP_CONTENT);
+            setFlags(0, flagsToUpdate);
+        } else {
+            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
+            requestFeature(FEATURE_NO_TITLE);
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
+            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
+            setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
+        }
+
+        WindowManager.LayoutParams params = getAttributes();
+
+        if (!hasSoftInputMode()) {
+            params.softInputMode = a.getInt(
+                    com.android.internal.R.styleable.Window_windowSoftInputMode,
+                    params.softInputMode);
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,
+                mIsFloating)) {
+            /* All dialogs should have the window dimmed */
+            if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
+                params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+            }
+            params.dimAmount = a.getFloat(
+                    android.R.styleable.Window_backgroundDimAmount, 0.5f);
+        }
+
+        if (params.windowAnimations == 0) {
+            params.windowAnimations = a.getResourceId(
+                    com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+        }
+
+        // The rest are only done if this window is not embedded; otherwise,
+        // the values are inherited from our container.
+        if (getContainer() == null) {
+            if (mBackgroundDrawable == null) {
+                if (mBackgroundResource == 0) {
+                    mBackgroundResource = a.getResourceId(
+                            com.android.internal.R.styleable.Window_windowBackground, 0);
+                }
+                if (mFrameResource == 0) {
+                    mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);
+                }
+                if (false) {
+                    System.out.println("Background: "
+                            + Integer.toHexString(mBackgroundResource) + " Frame: "
+                            + Integer.toHexString(mFrameResource));
+                }
+            }
+            mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
+        }
+
+        // Inflate the window decor.
+
+        int layoutResource;
+        int features = getLocalFeatures();
+        // System.out.println("Features: 0x" + Integer.toHexString(features));
+        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
+            if (mIsFloating) {
+                layoutResource = com.android.internal.R.layout.dialog_title_icons;
+            } else {
+                layoutResource = com.android.internal.R.layout.screen_title_icons;
+            }
+            // System.out.println("Title Icons!");
+        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {
+            // Special case for a window with only a progress bar (and title).
+            // XXX Need to have a no-title version of embedded windows.
+            layoutResource = com.android.internal.R.layout.screen_progress;
+            // System.out.println("Progress!");
+        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
+            // Special case for a window with a custom title.
+            // If the window is floating, we need a dialog layout
+            if (mIsFloating) {
+                layoutResource = com.android.internal.R.layout.dialog_custom_title;
+            } else {
+                layoutResource = com.android.internal.R.layout.screen_custom_title;
+            }
+        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
+            // If no other features and not embedded, only need a title.
+            // If the window is floating, we need a dialog layout
+            if (mIsFloating) {
+                layoutResource = com.android.internal.R.layout.dialog_title;
+            } else {
+                layoutResource = com.android.internal.R.layout.screen_title;
+            }
+            // System.out.println("Title!");
+        } else {
+            // Embedded, so no decoration is needed.
+            layoutResource = com.android.internal.R.layout.screen_simple;
+            // System.out.println("Simple!");
+        }
+
+        mDecor.startChanging();
+
+        View in = mLayoutInflater.inflate(layoutResource, null);
+        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
+        if (contentParent == null) {
+            throw new RuntimeException("Window couldn't find content container view");
+        }
+
+        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
+            ProgressBar progress = getCircularProgressBar(false);
+            if (progress != null) {
+                progress.setIndeterminate(true);
+            }
+        }
+
+        // Remaining setup -- of background and title -- that only applies
+        // to top-level windows.
+        if (getContainer() == null) {
+            Drawable drawable = mBackgroundDrawable;
+            if (mBackgroundResource != 0) {
+                drawable = getContext().getResources().getDrawable(mBackgroundResource);
+            }
+            mDecor.setWindowBackground(drawable);
+            drawable = null;
+            if (mFrameResource != 0) {
+                drawable = getContext().getResources().getDrawable(mFrameResource);
+            }
+            mDecor.setWindowFrame(drawable);
+
+            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
+            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
+            // " Title=" + Integer.toHexString(mTitleColor));
+
+            if (mTitleColor == 0) {
+                mTitleColor = mTextColor;
+            }
+
+            if (mTitle != null) {
+                setTitle(mTitle);
+            }
+            setTitleColor(mTitleColor);
+        }
+
+        mDecor.finishChanging();
+
+        return contentParent;
+    }
+
+    private void installDecor() {
+        if (mDecor == null) {
+            mDecor = generateDecor();
+            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+            mDecor.setIsRootNamespace(true);
+        }
+        if (mContentParent == null) {
+            mContentParent = generateLayout(mDecor);
+
+            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
+            if (mTitleView != null) {
+                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
+                    View titleContainer = findViewById(com.android.internal.R.id.title_container);
+                    if (titleContainer != null) {
+                        titleContainer.setVisibility(View.GONE);
+                    } else {
+                        mTitleView.setVisibility(View.GONE);
+                    }
+                    if (mContentParent instanceof FrameLayout) {
+                        ((FrameLayout)mContentParent).setForeground(null);
+                    }
+                } else {
+                    mTitleView.setText(mTitle);
+                }
+            }
+        }
+    }
+
+    private Drawable loadImageURI(Uri uri) {
+        try {
+            return Drawable.createFromStream(
+                    getContext().getContentResolver().openInputStream(uri), null);
+        } catch (Exception e) {
+            Log.w(TAG, "Unable to open content: " + uri);
+        }
+        return null;
+    }
+
+    private DrawableFeatureState getDrawableState(int featureId, boolean required) {
+        if ((getFeatures() & (1 << featureId)) == 0) {
+            if (!required) {
+                return null;
+            }
+            throw new RuntimeException("The feature has not been requested");
+        }
+
+        DrawableFeatureState[] ar;
+        if ((ar = mDrawables) == null || ar.length <= featureId) {
+            DrawableFeatureState[] nar = new DrawableFeatureState[featureId + 1];
+            if (ar != null) {
+                System.arraycopy(ar, 0, nar, 0, ar.length);
+            }
+            mDrawables = ar = nar;
+        }
+
+        DrawableFeatureState st = ar[featureId];
+        if (st == null) {
+            ar[featureId] = st = new DrawableFeatureState(featureId);
+        }
+        return st;
+    }
+
+    /**
+     * Gets a panel's state based on its feature ID.
+     *
+     * @param featureId The feature ID of the panel.
+     * @param required Whether the panel is required (if it is required and it
+     *            isn't in our features, this throws an exception).
+     * @return The panel state.
+     */
+    private PanelFeatureState getPanelState(int featureId, boolean required) {
+        return getPanelState(featureId, required, null);
+    }
+
+    /**
+     * Gets a panel's state based on its feature ID.
+     *
+     * @param featureId The feature ID of the panel.
+     * @param required Whether the panel is required (if it is required and it
+     *            isn't in our features, this throws an exception).
+     * @param convertPanelState Optional: If the panel state does not exist, use
+     *            this as the panel state.
+     * @return The panel state.
+     */
+    private PanelFeatureState getPanelState(int featureId, boolean required,
+            PanelFeatureState convertPanelState) {
+        if ((getFeatures() & (1 << featureId)) == 0) {
+            if (!required) {
+                return null;
+            }
+            throw new RuntimeException("The feature has not been requested");
+        }
+
+        PanelFeatureState[] ar;
+        if ((ar = mPanels) == null || ar.length <= featureId) {
+            PanelFeatureState[] nar = new PanelFeatureState[featureId + 1];
+            if (ar != null) {
+                System.arraycopy(ar, 0, nar, 0, ar.length);
+            }
+            mPanels = ar = nar;
+        }
+
+        PanelFeatureState st = ar[featureId];
+        if (st == null) {
+            ar[featureId] = st = (convertPanelState != null)
+                    ? convertPanelState
+                    : new PanelFeatureState(featureId);
+        }
+        return st;
+    }
+
+    @Override
+    public final void setChildDrawable(int featureId, Drawable drawable) {
+        DrawableFeatureState st = getDrawableState(featureId, true);
+        st.child = drawable;
+        updateDrawable(featureId, st, false);
+    }
+
+    @Override
+    public final void setChildInt(int featureId, int value) {
+        updateInt(featureId, value, false);
+    }
+
+    @Override
+    public boolean isShortcutKey(int keyCode, KeyEvent event) {
+        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+        return st.menu != null && st.menu.isShortcutKey(keyCode, event);
+    }
+
+    private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) {
+        // Do nothing if the decor is not yet installed... an update will
+        // need to be forced when we eventually become active.
+        if (mContentParent == null) {
+            return;
+        }
+
+        final int featureMask = 1 << featureId;
+
+        if ((getFeatures() & featureMask) == 0 && !fromResume) {
+            return;
+        }
+
+        Drawable drawable = null;
+        if (st != null) {
+            drawable = st.child;
+            if (drawable == null)
+                drawable = st.local;
+            if (drawable == null)
+                drawable = st.def;
+        }
+        if ((getLocalFeatures() & featureMask) == 0) {
+            if (getContainer() != null) {
+                if (isActive() || fromResume) {
+                    getContainer().setChildDrawable(featureId, drawable);
+                }
+            }
+        } else if (st != null && (st.cur != drawable || st.curAlpha != st.alpha)) {
+            // System.out.println("Drawable changed: old=" + st.cur
+            // + ", new=" + drawable);
+            st.cur = drawable;
+            st.curAlpha = st.alpha;
+            onDrawableChanged(featureId, drawable, st.alpha);
+        }
+    }
+
+    private void updateInt(int featureId, int value, boolean fromResume) {
+
+        // Do nothing if the decor is not yet installed... an update will
+        // need to be forced when we eventually become active.
+        if (mContentParent == null) {
+            return;
+        }
+
+        final int featureMask = 1 << featureId;
+
+        if ((getFeatures() & featureMask) == 0 && !fromResume) {
+            return;
+        }
+
+        if ((getLocalFeatures() & featureMask) == 0) {
+            if (getContainer() != null) {
+                getContainer().setChildInt(featureId, value);
+            }
+        } else {
+            onIntChanged(featureId, value);
+        }
+    }
+
+    private ImageView getLeftIconView() {
+        if (mLeftIconView != null) {
+            return mLeftIconView;
+        }
+        if (mContentParent == null) {
+            installDecor();
+        }
+        return (mLeftIconView = (ImageView)findViewById(com.android.internal.R.id.left_icon));
+    }
+
+    private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
+        if (mCircularProgressBar != null) {
+            return mCircularProgressBar;
+        }
+        if (mContentParent == null && shouldInstallDecor) {
+            installDecor();
+        }
+        mCircularProgressBar = (ProgressBar)findViewById(com.android.internal.R.id.progress_circular);
+        mCircularProgressBar.setVisibility(View.INVISIBLE);
+        return mCircularProgressBar;
+    }
+
+    private ProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
+        if (mHorizontalProgressBar != null) {
+            return mHorizontalProgressBar;
+        }
+        if (mContentParent == null && shouldInstallDecor) {
+            installDecor();
+        }
+        mHorizontalProgressBar = (ProgressBar)findViewById(com.android.internal.R.id.progress_horizontal);
+        mHorizontalProgressBar.setVisibility(View.INVISIBLE);
+        return mHorizontalProgressBar;
+    }
+
+    private ImageView getRightIconView() {
+        if (mRightIconView != null) {
+            return mRightIconView;
+        }
+        if (mContentParent == null) {
+            installDecor();
+        }
+        return (mRightIconView = (ImageView)findViewById(com.android.internal.R.id.right_icon));
+    }
+
+    /**
+     * Helper method for calling the {@link Callback#onPanelClosed(int, Menu)}
+     * callback. This method will grab whatever extra state is needed for the
+     * callback that isn't given in the parameters. If the panel is not open,
+     * this will not perform the callback.
+     *
+     * @param featureId Feature ID of the panel that was closed. Must be given.
+     * @param panel Panel that was closed. Optional but useful if there is no
+     *            menu given.
+     * @param menu The menu that was closed. Optional, but give if you have.
+     */
+    private void callOnPanelClosed(int featureId, PanelFeatureState panel, Menu menu) {
+        final Callback cb = getCallback();
+        if (cb == null)
+            return;
+
+        // Try to get a menu
+        if (menu == null) {
+            // Need a panel to grab the menu, so try to get that
+            if (panel == null) {
+                if ((featureId >= 0) && (featureId < mPanels.length)) {
+                    panel = mPanels[featureId];
+                }
+            }
+
+            if (panel != null) {
+                // menu still may be null, which is okay--we tried our best
+                menu = panel.menu;
+            }
+        }
+
+        // If the panel is not open, do not callback
+        if ((panel != null) && (!panel.isOpen))
+            return;
+
+        cb.onPanelClosed(featureId, menu);
+    }
+
+    /**
+     * Helper method for adding launch-search to most applications. Opens the
+     * search window using default settings.
+     *
+     * @return true if search window opened
+     */
+    private boolean launchDefaultSearch() {
+        final Callback cb = getCallback();
+        if (cb == null) {
+            return false;
+        } else {
+            sendCloseSystemWindows("search");
+            return cb.onSearchRequested();
+        }
+    }
+
+    @Override
+    public void setVolumeControlStream(int streamType) {
+        mVolumeControlStreamType = streamType;
+    }
+
+    @Override
+    public int getVolumeControlStream() {
+        return mVolumeControlStreamType;
+    }
+
+    private static final class DrawableFeatureState {
+        DrawableFeatureState(int _featureId) {
+            featureId = _featureId;
+        }
+
+        final int featureId;
+
+        int resid;
+
+        Uri uri;
+
+        Drawable local;
+
+        Drawable child;
+
+        Drawable def;
+
+        Drawable cur;
+
+        int alpha = 255;
+
+        int curAlpha = 255;
+    }
+
+    private static final class PanelFeatureState {
+
+        /** Feature ID for this panel. */
+        int featureId;
+
+        // Information pulled from the style for this panel.
+
+        int background;
+
+        /** The background when the panel spans the entire available width. */
+        int fullBackground;
+
+        int gravity;
+
+        int x;
+
+        int y;
+
+        int windowAnimations;
+
+        /** Dynamic state of the panel. */
+        DecorView decorView;
+
+        /** The panel that was returned by onCreatePanelView(). */
+        View createdPanelView;
+
+        /** The panel that we are actually showing. */
+        View shownPanelView;
+
+        /** Use {@link #setMenu} to set this. */
+        Menu menu;
+
+        /**
+         * Whether the panel has been prepared (see
+         * {@link PhoneWindow#preparePanel}).
+         */
+        boolean isPrepared;
+
+        /**
+         * Whether an item's action has been performed. This happens in obvious
+         * scenarios (user clicks on menu item), but can also happen with
+         * chording menu+(shortcut key).
+         */
+        boolean isHandled;
+
+        boolean isOpen;
+
+        /**
+         * True if the menu is in expanded mode, false if the menu is in icon
+         * mode
+         */
+        boolean isInExpandedMode;
+
+        public boolean qwertyMode;
+
+        boolean refreshDecorView;
+
+        boolean wasLastOpen;
+        
+        boolean wasLastExpanded;
+        
+        /**
+         * Contains the state of the menu when told to freeze.
+         */
+        Bundle frozenMenuState;
+
+        PanelFeatureState(int featureId) {
+            this.featureId = featureId;
+
+            refreshDecorView = false;
+        }
+
+        void setStyle(Context context) {
+            TypedArray a = context.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+            background = a.getResourceId(
+                    com.android.internal.R.styleable.Theme_panelBackground, 0);
+            fullBackground = a.getResourceId(
+                    com.android.internal.R.styleable.Theme_panelFullBackground, 0);
+            windowAnimations = a.getResourceId(
+                    com.android.internal.R.styleable.Theme_windowAnimationStyle, 0);
+            a.recycle();
+        }
+
+        void setMenu(Menu menu) {
+            this.menu = menu;
+
+            if (frozenMenuState != null) {
+                ((MenuBuilder) menu).restoreHierarchyState(frozenMenuState);
+                frozenMenuState = null;
+            }
+        }
+
+        Parcelable onSaveInstanceState() {
+            SavedState savedState = new SavedState();
+            savedState.featureId = featureId;
+            savedState.isOpen = isOpen;
+            savedState.isInExpandedMode = isInExpandedMode;
+
+            if (menu != null) {
+                savedState.menuState = new Bundle();
+                ((MenuBuilder) menu).saveHierarchyState(savedState.menuState);
+            }
+
+            return savedState;
+        }
+
+        void onRestoreInstanceState(Parcelable state) {
+            SavedState savedState = (SavedState) state;
+            featureId = savedState.featureId;
+            wasLastOpen = savedState.isOpen;
+            wasLastExpanded = savedState.isInExpandedMode;
+            frozenMenuState = savedState.menuState;
+
+            /*
+             * A LocalActivityManager keeps the same instance of this class around.
+             * The first time the menu is being shown after restoring, the
+             * Activity.onCreateOptionsMenu should be called. But, if it is the
+             * same instance then menu != null and we won't call that method.
+             * So, clear this.  Also clear any cached views.
+             */
+            menu = null;
+            createdPanelView = null;
+            shownPanelView = null;
+            decorView = null;
+        }
+
+        private static class SavedState implements Parcelable {
+            int featureId;
+            boolean isOpen;
+            boolean isInExpandedMode;
+            Bundle menuState;
+
+            public int describeContents() {
+                return 0;
+            }
+
+            public void writeToParcel(Parcel dest, int flags) {
+                dest.writeInt(featureId);
+                dest.writeInt(isOpen ? 1 : 0);
+                dest.writeInt(isInExpandedMode ? 1 : 0);
+
+                if (isOpen) {
+                    dest.writeBundle(menuState);
+                }
+            }
+
+            private static SavedState readFromParcel(Parcel source) {
+                SavedState savedState = new SavedState();
+                savedState.featureId = source.readInt();
+                savedState.isOpen = source.readInt() == 1;
+                savedState.isInExpandedMode = source.readInt() == 1;
+
+                if (savedState.isOpen) {
+                    savedState.menuState = source.readBundle();
+                }
+
+                return savedState;
+            }
+
+            public static final Parcelable.Creator<SavedState> CREATOR
+                    = new Parcelable.Creator<SavedState>() {
+                public SavedState createFromParcel(Parcel in) {
+                    return readFromParcel(in);
+                }
+
+                public SavedState[] newArray(int size) {
+                    return new SavedState[size];
+                }
+            };
+        }
+
+    }
+
+    /**
+     * Simple implementation of MenuBuilder.Callback that:
+     * <li> Opens a submenu when selected.
+     * <li> Calls back to the callback's onMenuItemSelected when an item is
+     * selected.
+     */
+    private final class ContextMenuCallback implements MenuBuilder.Callback {
+        private int mFeatureId;
+        private MenuDialogHelper mSubMenuHelper;
+
+        public ContextMenuCallback(int featureId) {
+            mFeatureId = featureId;
+        }
+
+        public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+            if (allMenusAreClosing) {
+                Callback callback = getCallback();
+                if (callback != null) callback.onPanelClosed(mFeatureId, menu);
+
+                if (menu == mContextMenu) {
+                    dismissContextMenu();
+                }
+
+                // Dismiss the submenu, if it is showing
+                if (mSubMenuHelper != null) {
+                    mSubMenuHelper.dismiss();
+                    mSubMenuHelper = null;
+                }
+            }
+        }
+
+        public void onCloseSubMenu(SubMenuBuilder menu) {
+            Callback callback = getCallback();
+            if (callback != null) callback.onPanelClosed(mFeatureId, menu.getRootMenu());
+        }
+
+        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
+            Callback callback = getCallback();
+            return (callback != null) && callback.onMenuItemSelected(mFeatureId, item);
+        }
+
+        public void onMenuModeChange(MenuBuilder menu) {
+        }
+
+        public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+            // Set a simple callback for the submenu
+            subMenu.setCallback(this);
+
+            // The window manager will give us a valid window token
+            mSubMenuHelper = new MenuDialogHelper(subMenu);
+            mSubMenuHelper.show(null);
+
+            return true;
+        }
+    }
+
+    void sendCloseSystemWindows() {
+        PhoneWindowManager.sendCloseSystemWindows(getContext(), null);
+    }
+
+    void sendCloseSystemWindows(String reason) {
+        PhoneWindowManager.sendCloseSystemWindows(getContext(), reason);
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/com/android/internal/policy/impl/PhoneWindowManager.java
new file mode 100755
index 0000000..cfdce5a
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -0,0 +1,2391 @@
+/*
+ * Copyright (C) 2006 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.policy.impl;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IStatusBar;
+import android.app.IUiModeManager;
+import android.app.UiModeManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.LocalPowerManager;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Vibrator;
+import android.provider.Settings;
+
+import com.android.internal.policy.PolicyManager;
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.widget.PointerLocationView;
+
+import android.util.Config;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.IWindowManager;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowOrientationListener;
+import android.view.RawInputEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.WindowManager;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import android.view.WindowManagerImpl;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.media.IAudioService;
+import android.media.AudioManager;
+
+import java.util.ArrayList;
+
+/**
+ * WindowManagerPolicy implementation for the Android phone UI.  This
+ * introduces a new method suffix, Lp, for an internal lock of the
+ * PhoneWindowManager.  This is used to protect some internal state, and
+ * can be acquired with either thw Lw and Li lock held, so has the restrictions
+ * of both of those when held.
+ */
+public class PhoneWindowManager implements WindowManagerPolicy {
+    static final String TAG = "WindowManager";
+    static final boolean DEBUG = false;
+    static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    static final boolean DEBUG_LAYOUT = false;
+    static final boolean SHOW_STARTING_ANIMATIONS = true;
+    static final boolean SHOW_PROCESSES_ON_ALT_MENU = false;
+    
+    // wallpaper is at the bottom, though the window manager may move it.
+    static final int WALLPAPER_LAYER = 2;
+    static final int APPLICATION_LAYER = 2;
+    static final int PHONE_LAYER = 3;
+    static final int SEARCH_BAR_LAYER = 4;
+    static final int STATUS_BAR_PANEL_LAYER = 5;
+    static final int SYSTEM_DIALOG_LAYER = 6;
+    // toasts and the plugged-in battery thing
+    static final int TOAST_LAYER = 7;
+    static final int STATUS_BAR_LAYER = 8;
+    // SIM errors and unlock.  Not sure if this really should be in a high layer.
+    static final int PRIORITY_PHONE_LAYER = 9;
+    // like the ANR / app crashed dialogs
+    static final int SYSTEM_ALERT_LAYER = 10;
+    // system-level error dialogs
+    static final int SYSTEM_ERROR_LAYER = 11;
+    // on-screen keyboards and other such input method user interfaces go here.
+    static final int INPUT_METHOD_LAYER = 12;
+    // on-screen keyboards and other such input method user interfaces go here.
+    static final int INPUT_METHOD_DIALOG_LAYER = 13;
+    // the keyguard; nothing on top of these can take focus, since they are
+    // responsible for power management when displayed.
+    static final int KEYGUARD_LAYER = 14;
+    static final int KEYGUARD_DIALOG_LAYER = 15;
+    // things in here CAN NOT take focus, but are shown on top of everything else.
+    static final int SYSTEM_OVERLAY_LAYER = 16;
+
+    static final int APPLICATION_MEDIA_SUBLAYER = -2;
+    static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
+    static final int APPLICATION_PANEL_SUBLAYER = 1;
+    static final int APPLICATION_SUB_PANEL_SUBLAYER = 2;
+
+    static final float SLIDE_TOUCH_EVENT_SIZE_LIMIT = 0.6f;
+    
+    // Debugging: set this to have the system act like there is no hard keyboard.
+    static final boolean KEYBOARD_ALWAYS_HIDDEN = false;
+    
+    static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
+    static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
+    static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+    static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
+
+    final Object mLock = new Object();
+    
+    Context mContext;
+    IWindowManager mWindowManager;
+    LocalPowerManager mPowerManager;
+    Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
+
+    // Vibrator pattern for haptic feedback of a long press.
+    long[] mLongPressVibePattern;
+    
+    // Vibrator pattern for haptic feedback of virtual key press.
+    long[] mVirtualKeyVibePattern;
+    
+    // Vibrator pattern for a short vibration.
+    long[] mKeyboardTapVibePattern;
+
+    // Vibrator pattern for haptic feedback during boot when safe mode is disabled.
+    long[] mSafeModeDisabledVibePattern;
+    
+    // Vibrator pattern for haptic feedback during boot when safe mode is enabled.
+    long[] mSafeModeEnabledVibePattern;
+
+    /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
+    boolean mEnableShiftMenuBugReports = false;
+    
+    boolean mSafeMode;
+    WindowState mStatusBar = null;
+    final ArrayList<WindowState> mStatusBarPanels = new ArrayList<WindowState>();
+    WindowState mKeyguard = null;
+    KeyguardViewMediator mKeyguardMediator;
+    GlobalActions mGlobalActions;
+    boolean mShouldTurnOffOnKeyUp;
+    RecentApplicationsDialog mRecentAppsDialog;
+    Handler mHandler;
+    
+    boolean mSystemReady;
+    boolean mLidOpen;
+    int mUiMode = Configuration.UI_MODE_TYPE_NORMAL;
+    int mLidOpenRotation;
+    int mCarDockRotation;
+    int mDeskDockRotation;
+    boolean mCarDockEnablesAccelerometer;
+    boolean mDeskDockEnablesAccelerometer;
+    int mLidKeyboardAccessibility;
+    int mLidNavigationAccessibility;
+    boolean mScreenOn = false;
+    boolean mOrientationSensorEnabled = false;
+    int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    static final int DEFAULT_ACCELEROMETER_ROTATION = 0;
+    int mAccelerometerDefault = DEFAULT_ACCELEROMETER_ROTATION;
+    boolean mHasSoftInput = false;
+    
+    int mPointerLocationMode = 0;
+    PointerLocationView mPointerLocationView = null;
+    
+    // The current size of the screen.
+    int mW, mH;
+    // During layout, the current screen borders with all outer decoration
+    // (status bar, input method dock) accounted for.
+    int mCurLeft, mCurTop, mCurRight, mCurBottom;
+    // During layout, the frame in which content should be displayed
+    // to the user, accounting for all screen decoration except for any
+    // space they deem as available for other content.  This is usually
+    // the same as mCur*, but may be larger if the screen decor has supplied
+    // content insets.
+    int mContentLeft, mContentTop, mContentRight, mContentBottom;
+    // During layout, the current screen borders along with input method
+    // windows are placed.
+    int mDockLeft, mDockTop, mDockRight, mDockBottom;
+    // During layout, the layer at which the doc window is placed.
+    int mDockLayer;
+    
+    static final Rect mTmpParentFrame = new Rect();
+    static final Rect mTmpDisplayFrame = new Rect();
+    static final Rect mTmpContentFrame = new Rect();
+    static final Rect mTmpVisibleFrame = new Rect();
+    
+    WindowState mTopFullscreenOpaqueWindowState;
+    boolean mForceStatusBar;
+    boolean mHideLockScreen;
+    boolean mDismissKeyguard;
+    boolean mHomePressed;
+    Intent mHomeIntent;
+    Intent mCarDockIntent;
+    Intent mDeskDockIntent;
+    boolean mSearchKeyPressed;
+    boolean mConsumeSearchKeyUp;
+
+    // support for activating the lock screen while the screen is on
+    boolean mAllowLockscreenWhenOn;
+    int mLockScreenTimeout;
+    boolean mLockScreenTimerActive;
+
+    // Behavior of ENDCALL Button.  (See Settings.System.END_BUTTON_BEHAVIOR.)
+    int mEndcallBehavior;
+
+    // Behavior of POWER button while in-call and screen on.
+    // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
+    int mIncallPowerBehavior;
+
+    int mLandscapeRotation = -1;
+    int mPortraitRotation = -1;
+
+    // Nothing to see here, move along...
+    int mFancyRotationAnimation;
+
+    ShortcutManager mShortcutManager;
+    PowerManager.WakeLock mBroadcastWakeLock;
+
+    class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.END_BUTTON_BEHAVIOR), false, this);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.ACCELEROMETER_ROTATION), false, this);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.SCREEN_OFF_TIMEOUT), false, this);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    Settings.System.POINTER_LOCATION), false, this);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
+            resolver.registerContentObserver(Settings.System.getUriFor(
+                    "fancy_rotation_anim"), false, this);
+            updateSettings();
+        }
+
+        @Override public void onChange(boolean selfChange) {
+            updateSettings();
+            try {
+                mWindowManager.setRotation(USE_LAST_ROTATION, false,
+                        mFancyRotationAnimation);
+            } catch (RemoteException e) {
+                // Ignore
+            }
+        }
+    }
+    
+    class MyOrientationListener extends WindowOrientationListener {
+        MyOrientationListener(Context context) {
+            super(context);
+        }
+        
+        @Override
+        public void onOrientationChanged(int rotation) {
+            // Send updates based on orientation value
+            if (localLOGV) Log.v(TAG, "onOrientationChanged, rotation changed to " +rotation);
+            try {
+                mWindowManager.setRotation(rotation, false,
+                        mFancyRotationAnimation);
+            } catch (RemoteException e) {
+                // Ignore
+
+            }
+        }                                      
+    }
+    MyOrientationListener mOrientationListener;
+
+    boolean useSensorForOrientationLp(int appOrientation) {
+        // The app says use the sensor.
+        if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
+            return true;
+        }
+        // The user preference says we can rotate, and the app is willing to rotate.
+        if (mAccelerometerDefault != 0 &&
+                (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
+                 || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) {
+            return true;
+        }
+        // We're in a dock that has a rotation affinity, an the app is willing to rotate.
+        if ((mCarDockEnablesAccelerometer && mUiMode == Configuration.UI_MODE_TYPE_CAR)
+                || (mDeskDockEnablesAccelerometer && mUiMode == Configuration.UI_MODE_TYPE_DESK)) {
+            // Note we override the nosensor flag here.
+            if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+                    || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+                return true;
+            }
+        }
+        // Else, don't use the sensor.
+        return false;
+    }
+    
+    /*
+     * We always let the sensor be switched on by default except when
+     * the user has explicitly disabled sensor based rotation or when the
+     * screen is switched off.
+     */
+    boolean needSensorRunningLp() {
+        if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR) {
+            // If the application has explicitly requested to follow the
+            // orientation, then we need to turn the sensor or.
+            return true;
+        }
+        if ((mCarDockEnablesAccelerometer && mUiMode == Configuration.UI_MODE_TYPE_CAR) ||
+            (mDeskDockEnablesAccelerometer && mUiMode == Configuration.UI_MODE_TYPE_DESK)) {
+            // enable accelerometer if we are docked in a dock that enables accelerometer
+            // orientation management,
+            return true;
+        }
+        if (mAccelerometerDefault == 0) {
+            // If the setting for using the sensor by default is enabled, then
+            // we will always leave it on.  Note that the user could go to
+            // a window that forces an orientation that does not use the
+            // sensor and in theory we could turn it off... however, when next
+            // turning it on we won't have a good value for the current
+            // orientation for a little bit, which can cause orientation
+            // changes to lag, so we'd like to keep it always on.  (It will
+            // still be turned off when the screen is off.)
+            return false;
+        }
+        return true;
+    }
+    
+    /*
+     * Various use cases for invoking this function
+     * screen turning off, should always disable listeners if already enabled
+     * screen turned on and current app has sensor based orientation, enable listeners 
+     * if not already enabled
+     * screen turned on and current app does not have sensor orientation, disable listeners if
+     * already enabled
+     * screen turning on and current app has sensor based orientation, enable listeners if needed
+     * screen turning on and current app has nosensor based orientation, do nothing
+     */
+    void updateOrientationListenerLp() {
+        if (!mOrientationListener.canDetectOrientation()) {
+            // If sensor is turned off or nonexistent for some reason
+            return;
+        }
+        //Could have been invoked due to screen turning on or off or
+        //change of the currently visible window's orientation
+        if (localLOGV) Log.v(TAG, "Screen status="+mScreenOn+
+                ", current orientation="+mCurrentAppOrientation+
+                ", SensorEnabled="+mOrientationSensorEnabled);
+        boolean disable = true;
+        if (mScreenOn) {
+            if (needSensorRunningLp()) {
+                disable = false;
+                //enable listener if not already enabled
+                if (!mOrientationSensorEnabled) {
+                    mOrientationListener.enable();
+                    if(localLOGV) Log.v(TAG, "Enabling listeners");
+                    mOrientationSensorEnabled = true;
+                }
+            } 
+        } 
+        //check if sensors need to be disabled
+        if (disable && mOrientationSensorEnabled) {
+            mOrientationListener.disable();
+            if(localLOGV) Log.v(TAG, "Disabling listeners");
+            mOrientationSensorEnabled = false;
+        }
+    }
+
+    Runnable mPowerLongPress = new Runnable() {
+        public void run() {
+            mShouldTurnOffOnKeyUp = false;
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
+            showGlobalActionsDialog();
+        }
+    };
+
+    void showGlobalActionsDialog() {
+        if (mGlobalActions == null) {
+            mGlobalActions = new GlobalActions(mContext);
+        }
+        final boolean keyguardShowing = mKeyguardMediator.isShowingAndNotHidden();
+        mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
+        if (keyguardShowing) {
+            // since it took two seconds of long press to bring this up,
+            // poke the wake lock so they have some time to see the dialog.
+            mKeyguardMediator.pokeWakelock();
+        }
+    }
+
+    boolean isDeviceProvisioned() {
+        return Settings.Secure.getInt(
+                mContext.getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
+    }
+
+    /**
+     * When a home-key longpress expires, close other system windows and launch the recent apps
+     */
+    Runnable mHomeLongPress = new Runnable() {
+        public void run() {
+            /*
+             * Eat the longpress so it won't dismiss the recent apps dialog when
+             * the user lets go of the home key
+             */
+            mHomePressed = false;
+            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
+            showRecentAppsDialog();
+        }
+    };
+
+    /**
+     * Create (if necessary) and launch the recent apps dialog
+     */
+    void showRecentAppsDialog() {
+        if (mRecentAppsDialog == null) {
+            mRecentAppsDialog = new RecentApplicationsDialog(mContext);
+        }
+        mRecentAppsDialog.show();
+    }
+    
+    /** {@inheritDoc} */
+    public void init(Context context, IWindowManager windowManager,
+            LocalPowerManager powerManager) {
+        mContext = context;
+        mWindowManager = windowManager;
+        mPowerManager = powerManager;
+        mKeyguardMediator = new KeyguardViewMediator(context, this, powerManager);
+        mHandler = new Handler();
+        mOrientationListener = new MyOrientationListener(mContext);
+        SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+        settingsObserver.observe();
+        mShortcutManager = new ShortcutManager(context, mHandler);
+        mShortcutManager.observe();
+        mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
+        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
+        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        mCarDockIntent =  new Intent(Intent.ACTION_MAIN, null);
+        mCarDockIntent.addCategory(Intent.CATEGORY_CAR_DOCK);
+        mCarDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        mDeskDockIntent =  new Intent(Intent.ACTION_MAIN, null);
+        mDeskDockIntent.addCategory(Intent.CATEGORY_DESK_DOCK);
+        mDeskDockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mBroadcastWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                "PhoneWindowManager.mBroadcastWakeLock");
+        mEnableShiftMenuBugReports = "1".equals(SystemProperties.get("ro.debuggable"));
+        mLidOpenRotation = readRotation(
+                com.android.internal.R.integer.config_lidOpenRotation);
+        mCarDockRotation = readRotation(
+                com.android.internal.R.integer.config_carDockRotation);
+        mDeskDockRotation = readRotation(
+                com.android.internal.R.integer.config_deskDockRotation);
+        mCarDockEnablesAccelerometer = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_carDockEnablesAccelerometer);
+        mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_deskDockEnablesAccelerometer);
+        mLidKeyboardAccessibility = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lidKeyboardAccessibility);
+        mLidNavigationAccessibility = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lidNavigationAccessibility);
+        // register for dock events
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
+        filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE);
+        filter.addAction(UiModeManager.ACTION_ENTER_DESK_MODE);
+        filter.addAction(UiModeManager.ACTION_EXIT_DESK_MODE);
+        context.registerReceiver(mDockReceiver, filter);
+        mVibrator = new Vibrator();
+        mLongPressVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_longPressVibePattern);
+        mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_virtualKeyVibePattern);
+        mKeyboardTapVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_keyboardTapVibePattern);
+        mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_safeModeDisabledVibePattern);
+        mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_safeModeEnabledVibePattern);
+    }
+
+    public void updateSettings() {
+        ContentResolver resolver = mContext.getContentResolver();
+        boolean updateRotation = false;
+        View addView = null;
+        View removeView = null;
+        synchronized (mLock) {
+            mEndcallBehavior = Settings.System.getInt(resolver,
+                    Settings.System.END_BUTTON_BEHAVIOR,
+                    Settings.System.END_BUTTON_BEHAVIOR_DEFAULT);
+            mIncallPowerBehavior = Settings.Secure.getInt(resolver,
+                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT);
+            mFancyRotationAnimation = Settings.System.getInt(resolver,
+                    "fancy_rotation_anim", 0) != 0 ? 0x80 : 0;
+            int accelerometerDefault = Settings.System.getInt(resolver,
+                    Settings.System.ACCELEROMETER_ROTATION, DEFAULT_ACCELEROMETER_ROTATION);
+            if (mAccelerometerDefault != accelerometerDefault) {
+                mAccelerometerDefault = accelerometerDefault;
+                updateOrientationListenerLp();
+            }
+            if (mSystemReady) {
+                int pointerLocation = Settings.System.getInt(resolver,
+                        Settings.System.POINTER_LOCATION, 0);
+                if (mPointerLocationMode != pointerLocation) {
+                    mPointerLocationMode = pointerLocation;
+                    if (pointerLocation != 0) {
+                        if (mPointerLocationView == null) {
+                            mPointerLocationView = new PointerLocationView(mContext);
+                            mPointerLocationView.setPrintCoords(false);
+                            addView = mPointerLocationView;
+                        }
+                    } else {
+                        removeView = mPointerLocationView;
+                        mPointerLocationView = null;
+                    }
+                }
+            }
+            // use screen off timeout setting as the timeout for the lockscreen
+            mLockScreenTimeout = Settings.System.getInt(resolver,
+                    Settings.System.SCREEN_OFF_TIMEOUT, 0);
+            String imId = Settings.Secure.getString(resolver,
+                    Settings.Secure.DEFAULT_INPUT_METHOD);
+            boolean hasSoftInput = imId != null && imId.length() > 0;
+            if (mHasSoftInput != hasSoftInput) {
+                mHasSoftInput = hasSoftInput;
+                updateRotation = true;
+            }
+        }
+        if (updateRotation) {
+            updateRotation(0);
+        }
+        if (addView != null) {
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    WindowManager.LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.MATCH_PARENT);
+            lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+            lp.flags = 
+                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+            lp.format = PixelFormat.TRANSLUCENT;
+            lp.setTitle("PointerLocation");
+            WindowManagerImpl wm = (WindowManagerImpl)
+                    mContext.getSystemService(Context.WINDOW_SERVICE);
+            wm.addView(addView, lp);
+        }
+        if (removeView != null) {
+            WindowManagerImpl wm = (WindowManagerImpl)
+                    mContext.getSystemService(Context.WINDOW_SERVICE);
+            wm.removeView(removeView);
+        }
+    }
+    
+    private int readRotation(int resID) {
+        try {
+            int rotation = mContext.getResources().getInteger(resID);
+            switch (rotation) {
+                case 0:
+                    return Surface.ROTATION_0;
+                case 90:
+                    return Surface.ROTATION_90;
+                case 180:
+                    return Surface.ROTATION_180;
+                case 270:
+                    return Surface.ROTATION_270;
+            }
+        } catch (Resources.NotFoundException e) {
+            // fall through
+        }
+        return -1;
+    }
+
+    /** {@inheritDoc} */
+    public int checkAddPermission(WindowManager.LayoutParams attrs) {
+        int type = attrs.type;
+        
+        if (type < WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
+                || type > WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
+            return WindowManagerImpl.ADD_OKAY;
+        }
+        String permission = null;
+        switch (type) {
+            case TYPE_TOAST:
+                // XXX right now the app process has complete control over
+                // this...  should introduce a token to let the system
+                // monitor/control what they are doing.
+                break;
+            case TYPE_INPUT_METHOD:
+            case TYPE_WALLPAPER:
+                // The window manager will check these.
+                break;
+            case TYPE_PHONE:
+            case TYPE_PRIORITY_PHONE:
+            case TYPE_SYSTEM_ALERT:
+            case TYPE_SYSTEM_ERROR:
+            case TYPE_SYSTEM_OVERLAY:
+                permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW;
+                break;
+            default:
+                permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+        }
+        if (permission != null) {
+            if (mContext.checkCallingOrSelfPermission(permission)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return WindowManagerImpl.ADD_PERMISSION_DENIED;
+            }
+        }
+        return WindowManagerImpl.ADD_OKAY;
+    }
+    
+    public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) {
+        switch (attrs.type) {
+            case TYPE_SYSTEM_OVERLAY:
+            case TYPE_TOAST:
+                // These types of windows can't receive input events.
+                attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+                break;
+        }
+    }
+    
+    void readLidState() {
+        try {
+            int sw = mWindowManager.getSwitchState(RawInputEvent.SW_LID);
+            if (sw >= 0) {
+                mLidOpen = sw == 0;
+            }
+        } catch (RemoteException e) {
+            // Ignore
+        }
+    }
+    
+    private int determineHiddenState(boolean lidOpen,
+            int mode, int hiddenValue, int visibleValue) {
+        switch (mode) {
+            case 1:
+                return lidOpen ? visibleValue : hiddenValue;
+            case 2:
+                return lidOpen ? hiddenValue : visibleValue;
+        }
+        return visibleValue;
+    }
+    
+    /** {@inheritDoc} */
+    public void adjustConfigurationLw(Configuration config) {
+        readLidState();
+        final boolean lidOpen = !KEYBOARD_ALWAYS_HIDDEN && mLidOpen;
+        mPowerManager.setKeyboardVisibility(lidOpen);
+        config.hardKeyboardHidden = determineHiddenState(lidOpen,
+                mLidKeyboardAccessibility, Configuration.HARDKEYBOARDHIDDEN_YES,
+                Configuration.HARDKEYBOARDHIDDEN_NO);
+        config.navigationHidden = determineHiddenState(lidOpen,
+                mLidNavigationAccessibility, Configuration.NAVIGATIONHIDDEN_YES,
+                Configuration.NAVIGATIONHIDDEN_NO);
+        config.keyboardHidden = (config.hardKeyboardHidden
+                        == Configuration.HARDKEYBOARDHIDDEN_NO || mHasSoftInput)
+                ? Configuration.KEYBOARDHIDDEN_NO
+                : Configuration.KEYBOARDHIDDEN_YES;
+    }
+    
+    public boolean isCheekPressedAgainstScreen(MotionEvent ev) {
+        if(ev.getSize() > SLIDE_TOUCH_EVENT_SIZE_LIMIT) {
+            return true;
+        }
+        int size = ev.getHistorySize();
+        for(int i = 0; i < size; i++) {
+            if(ev.getHistoricalSize(i) > SLIDE_TOUCH_EVENT_SIZE_LIMIT) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) {
+        if (mPointerLocationView == null) {
+            return;
+        }
+        synchronized (mLock) {
+            if (mPointerLocationView == null) {
+                return;
+            }
+            ev.offsetLocation(targetX, targetY);
+            mPointerLocationView.addTouchEvent(ev);
+            ev.offsetLocation(-targetX, -targetY);
+        }
+    }
+    
+    /** {@inheritDoc} */
+    public int windowTypeToLayerLw(int type) {
+        if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
+            return APPLICATION_LAYER;
+        }
+        switch (type) {
+        case TYPE_STATUS_BAR:
+            return STATUS_BAR_LAYER;
+        case TYPE_STATUS_BAR_PANEL:
+            return STATUS_BAR_PANEL_LAYER;
+        case TYPE_SYSTEM_DIALOG:
+            return SYSTEM_DIALOG_LAYER;
+        case TYPE_SEARCH_BAR:
+            return SEARCH_BAR_LAYER;
+        case TYPE_PHONE:
+            return PHONE_LAYER;
+        case TYPE_KEYGUARD:
+            return KEYGUARD_LAYER;
+        case TYPE_KEYGUARD_DIALOG:
+            return KEYGUARD_DIALOG_LAYER;
+        case TYPE_SYSTEM_ALERT:
+            return SYSTEM_ALERT_LAYER;
+        case TYPE_SYSTEM_ERROR:
+            return SYSTEM_ERROR_LAYER;
+        case TYPE_INPUT_METHOD:
+            return INPUT_METHOD_LAYER;
+        case TYPE_INPUT_METHOD_DIALOG:
+            return INPUT_METHOD_DIALOG_LAYER;
+        case TYPE_SYSTEM_OVERLAY:
+            return SYSTEM_OVERLAY_LAYER;
+        case TYPE_PRIORITY_PHONE:
+            return PRIORITY_PHONE_LAYER;
+        case TYPE_TOAST:
+            return TOAST_LAYER;
+        case TYPE_WALLPAPER:
+            return WALLPAPER_LAYER;
+        }
+        Log.e(TAG, "Unknown window type: " + type);
+        return APPLICATION_LAYER;
+    }
+
+    /** {@inheritDoc} */
+    public int subWindowTypeToLayerLw(int type) {
+        switch (type) {
+        case TYPE_APPLICATION_PANEL:
+        case TYPE_APPLICATION_ATTACHED_DIALOG:
+            return APPLICATION_PANEL_SUBLAYER;
+        case TYPE_APPLICATION_MEDIA:
+            return APPLICATION_MEDIA_SUBLAYER;
+        case TYPE_APPLICATION_MEDIA_OVERLAY:
+            return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
+        case TYPE_APPLICATION_SUB_PANEL:
+            return APPLICATION_SUB_PANEL_SUBLAYER;
+        }
+        Log.e(TAG, "Unknown sub-window type: " + type);
+        return 0;
+    }
+
+    public int getMaxWallpaperLayer() {
+        return STATUS_BAR_LAYER;
+    }
+
+    public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) {
+        return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD;
+    }
+    
+    public boolean canBeForceHidden(WindowState win, WindowManager.LayoutParams attrs) {
+        return attrs.type != WindowManager.LayoutParams.TYPE_STATUS_BAR
+                && attrs.type != WindowManager.LayoutParams.TYPE_WALLPAPER;
+    }
+    
+    /** {@inheritDoc} */
+    public View addStartingWindow(IBinder appToken, String packageName,
+                                  int theme, CharSequence nonLocalizedLabel,
+                                  int labelRes, int icon) {
+        if (!SHOW_STARTING_ANIMATIONS) {
+            return null;
+        }
+        if (packageName == null) {
+            return null;
+        }
+        
+    	Context context = mContext;
+    	boolean setTheme = false;
+    	//Log.i(TAG, "addStartingWindow " + packageName + ": nonLocalizedLabel="
+    	//        + nonLocalizedLabel + " theme=" + Integer.toHexString(theme));
+    	if (theme != 0 || labelRes != 0) {
+    	    try {
+    	        context = context.createPackageContext(packageName, 0);
+    	        if (theme != 0) {
+    	            context.setTheme(theme);
+    	            setTheme = true;
+    	        }
+    	    } catch (PackageManager.NameNotFoundException e) {
+                // Ignore
+            }
+    	}
+    	if (!setTheme) {
+    	    context.setTheme(com.android.internal.R.style.Theme);
+    	}
+    	
+        Window win = PolicyManager.makeNewWindow(context);
+        if (win.getWindowStyle().getBoolean(
+                com.android.internal.R.styleable.Window_windowDisablePreview, false)) {
+            return null;
+        }
+        
+        Resources r = context.getResources();
+        win.setTitle(r.getText(labelRes, nonLocalizedLabel));
+
+        win.setType(
+            WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+        // Force the window flags: this is a fake window, so it is not really
+        // touchable or focusable by the user.  We also add in the ALT_FOCUSABLE_IM
+        // flag because we do know that the next window will take input
+        // focus, so we want to get the IME window up on top of us right away.
+        win.setFlags(
+            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
+            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
+            WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE|
+            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
+            WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+
+        win.setLayout(WindowManager.LayoutParams.MATCH_PARENT,
+                            WindowManager.LayoutParams.MATCH_PARENT);
+
+        final WindowManager.LayoutParams params = win.getAttributes();
+        params.token = appToken;
+        params.packageName = packageName;
+        params.windowAnimations = win.getWindowStyle().getResourceId(
+                com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+        params.setTitle("Starting " + packageName);
+
+        try {
+            WindowManagerImpl wm = (WindowManagerImpl)
+                    context.getSystemService(Context.WINDOW_SERVICE);
+            View view = win.getDecorView();
+
+            if (win.isFloating()) {
+                // Whoops, there is no way to display an animation/preview
+                // of such a thing!  After all that work...  let's skip it.
+                // (Note that we must do this here because it is in
+                // getDecorView() where the theme is evaluated...  maybe
+                // we should peek the floating attribute from the theme
+                // earlier.)
+                return null;
+            }
+            
+            if (localLOGV) Log.v(
+                TAG, "Adding starting window for " + packageName
+                + " / " + appToken + ": "
+                + (view.getParent() != null ? view : null));
+
+            wm.addView(view, params);
+
+            // Only return the view if it was successfully added to the
+            // window manager... which we can tell by it having a parent.
+            return view.getParent() != null ? view : null;
+        } catch (WindowManagerImpl.BadTokenException e) {
+            // ignore
+            Log.w(TAG, appToken + " already running, starting window not displayed");
+        }
+
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public void removeStartingWindow(IBinder appToken, View window) {
+        // RuntimeException e = new RuntimeException();
+        // Log.i(TAG, "remove " + appToken + " " + window, e);
+
+        if (localLOGV) Log.v(
+            TAG, "Removing starting window for " + appToken + ": " + window);
+
+        if (window != null) {
+            WindowManagerImpl wm = (WindowManagerImpl) mContext.getSystemService(Context.WINDOW_SERVICE);
+            wm.removeView(window);
+        }
+    }
+
+    /**
+     * Preflight adding a window to the system.
+     * 
+     * Currently enforces that three window types are singletons:
+     * <ul>
+     * <li>STATUS_BAR_TYPE</li>
+     * <li>KEYGUARD_TYPE</li>
+     * </ul>
+     * 
+     * @param win The window to be added
+     * @param attrs Information about the window to be added
+     * 
+     * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons, WindowManagerImpl.ADD_MULTIPLE_SINGLETON
+     */
+    public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
+        switch (attrs.type) {
+            case TYPE_STATUS_BAR:
+                if (mStatusBar != null) {
+                    return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
+                }
+                mStatusBar = win;
+                break;
+            case TYPE_STATUS_BAR_PANEL:
+                mStatusBarPanels.add(win);
+                break;
+            case TYPE_KEYGUARD:
+                if (mKeyguard != null) {
+                    return WindowManagerImpl.ADD_MULTIPLE_SINGLETON;
+                }
+                mKeyguard = win;
+                break;
+        }
+        return WindowManagerImpl.ADD_OKAY;
+    }
+
+    /** {@inheritDoc} */
+    public void removeWindowLw(WindowState win) {
+        if (mStatusBar == win) {
+            mStatusBar = null;
+        }
+        else if (mKeyguard == win) {
+            mKeyguard = null;
+        } else {
+            mStatusBarPanels.remove(win);
+        }
+    }
+
+    static final boolean PRINT_ANIM = false;
+    
+    /** {@inheritDoc} */
+    public int selectAnimationLw(WindowState win, int transit) {
+        if (PRINT_ANIM) Log.i(TAG, "selectAnimation in " + win
+              + ": transit=" + transit);
+        if (transit == TRANSIT_PREVIEW_DONE) {
+            if (win.hasAppShownWindows()) {
+                if (PRINT_ANIM) Log.i(TAG, "**** STARTING EXIT");
+                return com.android.internal.R.anim.app_starting_exit;
+            }
+        }
+
+        return 0;
+    }
+
+    public Animation createForceHideEnterAnimation() {
+        return AnimationUtils.loadAnimation(mContext,
+                com.android.internal.R.anim.lock_screen_behind_enter);
+    }
+    
+    static ITelephony getPhoneInterface() {
+        return ITelephony.Stub.asInterface(ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+    }
+
+    static IAudioService getAudioInterface() {
+        return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE));
+    }
+
+    boolean keyguardOn() {
+        return keyguardIsShowingTq() || inKeyguardRestrictedKeyInputMode();
+    }
+
+    private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
+            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
+            WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+        };
+
+    /** {@inheritDoc} */
+    public boolean interceptKeyTi(WindowState win, int code, int metaKeys, boolean down, 
+            int repeatCount, int flags) {
+        boolean keyguardOn = keyguardOn();
+
+        if (false) {
+            Log.d(TAG, "interceptKeyTi code=" + code + " down=" + down + " repeatCount="
+                    + repeatCount + " keyguardOn=" + keyguardOn + " mHomePressed=" + mHomePressed);
+        }
+
+        // Clear a pending HOME longpress if the user releases Home
+        // TODO: This could probably be inside the next bit of logic, but that code
+        // turned out to be a bit fragile so I'm doing it here explicitly, for now.
+        if ((code == KeyEvent.KEYCODE_HOME) && !down) {
+            mHandler.removeCallbacks(mHomeLongPress);
+        }
+
+        // If the HOME button is currently being held, then we do special
+        // chording with it.
+        if (mHomePressed) {
+            
+            // If we have released the home key, and didn't do anything else
+            // while it was pressed, then it is time to go home!
+            if (code == KeyEvent.KEYCODE_HOME) {
+                if (!down) {
+                    mHomePressed = false;
+                    
+                    if ((flags&KeyEvent.FLAG_CANCELED) == 0) {
+                        // If an incoming call is ringing, HOME is totally disabled.
+                        // (The user is already on the InCallScreen at this point,
+                        // and his ONLY options are to answer or reject the call.)
+                        boolean incomingRinging = false;
+                        try {
+                            ITelephony phoneServ = getPhoneInterface();
+                            if (phoneServ != null) {
+                                incomingRinging = phoneServ.isRinging();
+                            } else {
+                                Log.w(TAG, "Unable to find ITelephony interface");
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+                        }
+        
+                        if (incomingRinging) {
+                            Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+                        } else {
+                            launchHomeFromHotKey();
+                        }
+                    } else {
+                        Log.i(TAG, "Ignoring HOME; event canceled.");
+                    }
+                }
+            }
+            
+            return true;
+        }
+        
+        // First we always handle the home key here, so applications
+        // can never break it, although if keyguard is on, we do let
+        // it handle it, because that gives us the correct 5 second
+        // timeout.
+        if (code == KeyEvent.KEYCODE_HOME) {
+
+            // If a system window has focus, then it doesn't make sense
+            // right now to interact with applications.
+            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
+            if (attrs != null) {
+                final int type = attrs.type;
+                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
+                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
+                    // the "app" is keyguard, so give it the key
+                    return false;
+                }
+                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
+                for (int i=0; i<typeCount; i++) {
+                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
+                        // don't do anything, but also don't pass it to the app
+                        return true;
+                    }
+                }
+            }
+            
+            if (down && repeatCount == 0) {
+                if (!keyguardOn) {
+                    mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
+                }
+                mHomePressed = true;
+            }
+            return true;
+        } else if (code == KeyEvent.KEYCODE_MENU) {
+            // Hijack modified menu keys for debugging features
+            final int chordBug = KeyEvent.META_SHIFT_ON;
+
+            if (down && repeatCount == 0) {
+                if (mEnableShiftMenuBugReports && (metaKeys & chordBug) == chordBug) {
+                    Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
+                    mContext.sendOrderedBroadcast(intent, null);
+                    return true;
+                } else if (SHOW_PROCESSES_ON_ALT_MENU &&
+                        (metaKeys & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
+                    Intent service = new Intent();
+                    service.setClassName(mContext, "com.android.server.LoadAverageService");
+                    ContentResolver res = mContext.getContentResolver();
+                    boolean shown = Settings.System.getInt(
+                            res, Settings.System.SHOW_PROCESSES, 0) != 0;
+                    if (!shown) {
+                        mContext.startService(service);
+                    } else {
+                        mContext.stopService(service);
+                    }
+                    Settings.System.putInt(
+                            res, Settings.System.SHOW_PROCESSES, shown ? 0 : 1);
+                    return true;
+                }
+            }
+        } else if (code == KeyEvent.KEYCODE_NOTIFICATION) {
+            if (down) {
+                // this key doesn't exist on current hardware, but if a device
+                // didn't have a touchscreen, it would want one of these to open
+                // the status bar.
+                IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar"));
+                if (sbs != null) {
+                    try {
+                        sbs.toggle();
+                    } catch (RemoteException e) {
+                        // we're screwed anyway, since it's in this process
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+            return true;
+        } else if (code == KeyEvent.KEYCODE_SEARCH) {
+            if (down) {
+                if (repeatCount == 0) {
+                    mSearchKeyPressed = true;
+                }
+            } else {
+                mSearchKeyPressed = false;
+                
+                if (mConsumeSearchKeyUp) {
+                    // Consume the up-event
+                    mConsumeSearchKeyUp = false;
+                    return true;
+                }
+            }
+        }
+        
+        // Shortcuts are invoked through Search+key, so intercept those here
+        if (mSearchKeyPressed) {
+            if (down && repeatCount == 0 && !keyguardOn) {
+                Intent shortcutIntent = mShortcutManager.getIntent(code, metaKeys);
+                if (shortcutIntent != null) {
+                    shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    mContext.startActivity(shortcutIntent);
+                    
+                    /*
+                     * We launched an app, so the up-event of the search key
+                     * should be consumed
+                     */
+                    mConsumeSearchKeyUp = true;
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * A home key -> launch home action was detected.  Take the appropriate action
+     * given the situation with the keyguard.
+     */
+    void launchHomeFromHotKey() {
+        if (mKeyguardMediator.isShowingAndNotHidden()) {
+            // don't launch home if keyguard showing
+        } else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) {
+            // when in keyguard restricted mode, must first verify unlock
+            // before launching home
+            mKeyguardMediator.verifyUnlock(new OnKeyguardExitResult() {
+                public void onKeyguardExitResult(boolean success) {
+                    if (success) {
+                        try {
+                            ActivityManagerNative.getDefault().stopAppSwitches();
+                        } catch (RemoteException e) {
+                        }
+                        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+                        startDockOrHome();
+                    }
+                }
+            });
+        } else {
+            // no keyguard stuff to worry about, just launch home!
+            try {
+                ActivityManagerNative.getDefault().stopAppSwitches();
+            } catch (RemoteException e) {
+            }
+            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+            startDockOrHome();
+        }
+    }
+
+    public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) {
+        final int fl = attrs.flags;
+        
+        if ((fl &
+                (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
+                == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+            contentInset.set(mCurLeft, mCurTop, mW - mCurRight, mH - mCurBottom);
+        } else {
+            contentInset.setEmpty();
+        }
+    }
+    
+    /** {@inheritDoc} */
+    public void beginLayoutLw(int displayWidth, int displayHeight) {
+        mW = displayWidth;
+        mH = displayHeight;
+        mDockLeft = mContentLeft = mCurLeft = 0;
+        mDockTop = mContentTop = mCurTop = 0;
+        mDockRight = mContentRight = mCurRight = displayWidth;
+        mDockBottom = mContentBottom = mCurBottom = displayHeight;
+        mDockLayer = 0x10000000;
+
+        // decide where the status bar goes ahead of time
+        if (mStatusBar != null) {
+            final Rect pf = mTmpParentFrame;
+            final Rect df = mTmpDisplayFrame;
+            final Rect vf = mTmpVisibleFrame;
+            pf.left = df.left = vf.left = 0;
+            pf.top = df.top = vf.top = 0;
+            pf.right = df.right = vf.right = displayWidth;
+            pf.bottom = df.bottom = vf.bottom = displayHeight;
+            
+            mStatusBar.computeFrameLw(pf, df, vf, vf);
+            if (mStatusBar.isVisibleLw()) {
+                // If the status bar is hidden, we don't want to cause
+                // windows behind it to scroll.
+                mDockTop = mContentTop = mCurTop = mStatusBar.getFrameLw().bottom;
+                if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mDockBottom="
+                        + mDockBottom + " mContentBottom="
+                        + mContentBottom + " mCurBottom=" + mCurBottom);
+            }
+        }
+    }
+
+    void setAttachedWindowFrames(WindowState win, int fl, int sim,
+            WindowState attached, boolean insetDecors, Rect pf, Rect df, Rect cf, Rect vf) {
+        if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) {
+            // Here's a special case: if this attached window is a panel that is
+            // above the dock window, and the window it is attached to is below
+            // the dock window, then the frames we computed for the window it is
+            // attached to can not be used because the dock is effectively part
+            // of the underlying window and the attached window is floating on top
+            // of the whole thing.  So, we ignore the attached window and explicitly
+            // compute the frames that would be appropriate without the dock.
+            df.left = cf.left = vf.left = mDockLeft;
+            df.top = cf.top = vf.top = mDockTop;
+            df.right = cf.right = vf.right = mDockRight;
+            df.bottom = cf.bottom = vf.bottom = mDockBottom;
+        } else {
+            // The effective display frame of the attached window depends on
+            // whether it is taking care of insetting its content.  If not,
+            // we need to use the parent's content frame so that the entire
+            // window is positioned within that content.  Otherwise we can use
+            // the display frame and let the attached window take care of
+            // positioning its content appropriately.
+            if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
+                cf.set(attached.getDisplayFrameLw());
+            } else {
+                // If the window is resizing, then we want to base the content
+                // frame on our attached content frame to resize...  however,
+                // things can be tricky if the attached window is NOT in resize
+                // mode, in which case its content frame will be larger.
+                // Ungh.  So to deal with that, make sure the content frame
+                // we end up using is not covering the IM dock.
+                cf.set(attached.getContentFrameLw());
+                if (attached.getSurfaceLayer() < mDockLayer) {
+                    if (cf.left < mContentLeft) cf.left = mContentLeft;
+                    if (cf.top < mContentTop) cf.top = mContentTop;
+                    if (cf.right > mContentRight) cf.right = mContentRight;
+                    if (cf.bottom > mContentBottom) cf.bottom = mContentBottom;
+                }
+            }
+            df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
+            vf.set(attached.getVisibleFrameLw());
+        }
+        // The LAYOUT_IN_SCREEN flag is used to determine whether the attached
+        // window should be positioned relative to its parent or the entire
+        // screen.
+        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0
+                ? attached.getFrameLw() : df);
+    }
+    
+    /** {@inheritDoc} */
+    public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs,
+            WindowState attached) {
+        // we've already done the status bar
+        if (win == mStatusBar) {
+            return;
+        }
+
+        if (false) {
+            if ("com.google.android.youtube".equals(attrs.packageName)
+                    && attrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+                Log.i(TAG, "GOTCHA!");
+            }
+        }
+        
+        final int fl = attrs.flags;
+        final int sim = attrs.softInputMode;
+        
+        final Rect pf = mTmpParentFrame;
+        final Rect df = mTmpDisplayFrame;
+        final Rect cf = mTmpContentFrame;
+        final Rect vf = mTmpVisibleFrame;
+        
+        if (attrs.type == TYPE_INPUT_METHOD) {
+            pf.left = df.left = cf.left = vf.left = mDockLeft;
+            pf.top = df.top = cf.top = vf.top = mDockTop;
+            pf.right = df.right = cf.right = vf.right = mDockRight;
+            pf.bottom = df.bottom = cf.bottom = vf.bottom = mDockBottom;
+            // IM dock windows always go to the bottom of the screen.
+            attrs.gravity = Gravity.BOTTOM;
+            mDockLayer = win.getSurfaceLayer();
+        } else {
+            if ((fl &
+                    (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))
+                    == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+                // This is the case for a normal activity window: we want it
+                // to cover all of the screen space, and it can take care of
+                // moving its contents to account for screen decorations that
+                // intrude into that space.
+                if (attached != null) {
+                    // If this window is attached to another, our display
+                    // frame is the same as the one we are attached to.
+                    setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);
+                } else {
+                    pf.left = df.left = 0;
+                    pf.top = df.top = 0;
+                    pf.right = df.right = mW;
+                    pf.bottom = df.bottom = mH;
+                    if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
+                        cf.left = mDockLeft;
+                        cf.top = mDockTop;
+                        cf.right = mDockRight;
+                        cf.bottom = mDockBottom;
+                    } else {
+                        cf.left = mContentLeft;
+                        cf.top = mContentTop;
+                        cf.right = mContentRight;
+                        cf.bottom = mContentBottom;
+                    }
+                    vf.left = mCurLeft;
+                    vf.top = mCurTop;
+                    vf.right = mCurRight;
+                    vf.bottom = mCurBottom;
+                }
+            } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) {
+                // A window that has requested to fill the entire screen just
+                // gets everything, period.
+                pf.left = df.left = cf.left = 0;
+                pf.top = df.top = cf.top = 0;
+                pf.right = df.right = cf.right = mW;
+                pf.bottom = df.bottom = cf.bottom = mH;
+                vf.left = mCurLeft;
+                vf.top = mCurTop;
+                vf.right = mCurRight;
+                vf.bottom = mCurBottom;
+            } else if (attached != null) {
+                // A child window should be placed inside of the same visible
+                // frame that its parent had.
+                setAttachedWindowFrames(win, fl, sim, attached, false, pf, df, cf, vf);
+            } else {
+                // Otherwise, a normal window must be placed inside the content
+                // of all screen decorations.
+                pf.left = mContentLeft;
+                pf.top = mContentTop;
+                pf.right = mContentRight;
+                pf.bottom = mContentBottom;
+                if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {
+                    df.left = cf.left = mDockLeft;
+                    df.top = cf.top = mDockTop;
+                    df.right = cf.right = mDockRight;
+                    df.bottom = cf.bottom = mDockBottom;
+                } else {
+                    df.left = cf.left = mContentLeft;
+                    df.top = cf.top = mContentTop;
+                    df.right = cf.right = mContentRight;
+                    df.bottom = cf.bottom = mContentBottom;
+                }
+                vf.left = mCurLeft;
+                vf.top = mCurTop;
+                vf.right = mCurRight;
+                vf.bottom = mCurBottom;
+            }
+        }
+        
+        if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0) {
+            df.left = df.top = cf.left = cf.top = vf.left = vf.top = -10000;
+            df.right = df.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
+        }
+
+        if (DEBUG_LAYOUT) Log.v(TAG, "Compute frame " + attrs.getTitle()
+                + ": sim=#" + Integer.toHexString(sim)
+                + " pf=" + pf.toShortString() + " df=" + df.toShortString()
+                + " cf=" + cf.toShortString() + " vf=" + vf.toShortString());
+        
+        if (false) {
+            if ("com.google.android.youtube".equals(attrs.packageName)
+                    && attrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+                if (true || localLOGV) Log.v(TAG, "Computing frame of " + win +
+                        ": sim=#" + Integer.toHexString(sim)
+                        + " pf=" + pf.toShortString() + " df=" + df.toShortString()
+                        + " cf=" + cf.toShortString() + " vf=" + vf.toShortString());
+            }
+        }
+        
+        win.computeFrameLw(pf, df, cf, vf);
+        
+        // Dock windows carve out the bottom of the screen, so normal windows
+        // can't appear underneath them.
+        if (attrs.type == TYPE_INPUT_METHOD && !win.getGivenInsetsPendingLw()) {
+            int top = win.getContentFrameLw().top;
+            top += win.getGivenContentInsetsLw().top;
+            if (mContentBottom > top) {
+                mContentBottom = top;
+            }
+            top = win.getVisibleFrameLw().top;
+            top += win.getGivenVisibleInsetsLw().top;
+            if (mCurBottom > top) {
+                mCurBottom = top;
+            }
+            if (DEBUG_LAYOUT) Log.v(TAG, "Input method: mDockBottom="
+                    + mDockBottom + " mContentBottom="
+                    + mContentBottom + " mCurBottom=" + mCurBottom);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public int finishLayoutLw() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    public void beginAnimationLw(int displayWidth, int displayHeight) {
+        mTopFullscreenOpaqueWindowState = null;
+        mForceStatusBar = false;
+        
+        mHideLockScreen = false;
+        mAllowLockscreenWhenOn = false;
+        mDismissKeyguard = false;
+    }
+
+    /** {@inheritDoc} */
+    public void animatingWindowLw(WindowState win,
+                                WindowManager.LayoutParams attrs) {
+        if (mTopFullscreenOpaqueWindowState == null &&
+                win.isVisibleOrBehindKeyguardLw()) {
+            if ((attrs.flags & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
+                mForceStatusBar = true;
+            } 
+            if (attrs.type >= FIRST_APPLICATION_WINDOW
+                    && attrs.type <= LAST_APPLICATION_WINDOW
+                    && win.fillsScreenLw(mW, mH, false, false)) {
+                if (DEBUG_LAYOUT) Log.v(TAG, "Fullscreen window: " + win);
+                mTopFullscreenOpaqueWindowState = win;
+                if ((attrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
+                    if (localLOGV) Log.v(TAG, "Setting mHideLockScreen to true by win " + win);
+                    mHideLockScreen = true;
+                }
+                if ((attrs.flags & FLAG_DISMISS_KEYGUARD) != 0) {
+                    if (localLOGV) Log.v(TAG, "Setting mDismissKeyguard to true by win " + win);
+                    mDismissKeyguard = true;
+                }
+                if ((attrs.flags & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
+                    mAllowLockscreenWhenOn = true;
+                }
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public int finishAnimationLw() {
+        int changes = 0;
+        
+        boolean hiding = false;
+        if (mStatusBar != null) {
+            if (localLOGV) Log.i(TAG, "force=" + mForceStatusBar
+                    + " top=" + mTopFullscreenOpaqueWindowState);
+            if (mForceStatusBar) {
+                if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar");
+                if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
+            } else if (mTopFullscreenOpaqueWindowState != null) {
+                //Log.i(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+                //        + " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw());
+                //Log.i(TAG, "attr: " + mTopFullscreenOpaqueWindowState.getAttrs());
+                WindowManager.LayoutParams lp =
+                    mTopFullscreenOpaqueWindowState.getAttrs();
+                boolean hideStatusBar =
+                    (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
+                if (hideStatusBar) {
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Hiding status bar");
+                    if (mStatusBar.hideLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                    hiding = true;
+                } else {
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar");
+                    if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT;
+                }
+            }
+        }
+        
+        if (changes != 0 && hiding) {
+            IStatusBar sbs = IStatusBar.Stub.asInterface(ServiceManager.getService("statusbar"));
+            if (sbs != null) {
+                try {
+                    // Make sure the window shade is hidden.
+                    sbs.deactivate();
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        // Hide the key guard if a visible window explicitly specifies that it wants to be displayed
+        // when the screen is locked
+        if (mKeyguard != null) {
+            if (localLOGV) Log.v(TAG, "finishLayoutLw::mHideKeyguard="+mHideLockScreen);
+            if (mDismissKeyguard && !mKeyguardMediator.isSecure()) {
+                if (mKeyguard.hideLw(true)) {
+                    changes |= FINISH_LAYOUT_REDO_LAYOUT
+                            | FINISH_LAYOUT_REDO_CONFIG
+                            | FINISH_LAYOUT_REDO_WALLPAPER;
+                }
+                if (mKeyguardMediator.isShowing()) {
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            mKeyguardMediator.keyguardDone(false, false);
+                        }
+                    });
+                }
+            } else if (mHideLockScreen) {
+                if (mKeyguard.hideLw(true)) {
+                    changes |= FINISH_LAYOUT_REDO_LAYOUT
+                            | FINISH_LAYOUT_REDO_CONFIG
+                            | FINISH_LAYOUT_REDO_WALLPAPER;
+                }
+                mKeyguardMediator.setHidden(true);
+            } else {
+                if (mKeyguard.showLw(true)) {
+                    changes |= FINISH_LAYOUT_REDO_LAYOUT
+                            | FINISH_LAYOUT_REDO_CONFIG
+                            | FINISH_LAYOUT_REDO_WALLPAPER;
+                }
+                mKeyguardMediator.setHidden(false);
+            }
+        }
+        
+        // update since mAllowLockscreenWhenOn might have changed
+        updateLockScreenTimeout();
+        return changes;
+    }
+
+    public boolean allowAppAnimationsLw() {
+        if (mKeyguard != null && mKeyguard.isVisibleLw()) {
+            // If keyguard is currently visible, no reason to animate
+            // behind it.
+            return false;
+        }
+        if (mStatusBar != null && mStatusBar.isVisibleLw()) {
+            Rect rect = new Rect(mStatusBar.getShownFrameLw());
+            for (int i=mStatusBarPanels.size()-1; i>=0; i--) {
+                WindowState w = mStatusBarPanels.get(i);
+                if (w.isVisibleLw()) {
+                    rect.union(w.getShownFrameLw());
+                }
+            }
+            final int insetw = mW/10;
+            final int inseth = mH/10;
+            if (rect.contains(insetw, inseth, mW-insetw, mH-inseth)) {
+                // All of the status bar windows put together cover the
+                // screen, so the app can't be seen.  (Note this test doesn't
+                // work if the rects of these windows are at off offsets or
+                // sizes, causing gaps in the rect union we have computed.)
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public boolean preprocessInputEventTq(RawInputEvent event) {
+        switch (event.type) {
+            case RawInputEvent.EV_SW:
+                if (event.keycode == RawInputEvent.SW_LID) {
+                    // lid changed state
+                    mLidOpen = event.value == 0;
+                    boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen);
+                    updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
+                    if (awakeNow) {
+                        // If the lid opening and we don't have to keep the
+                        // keyguard up, then we can turn on the screen
+                        // immediately.
+                        mKeyguardMediator.pokeWakelock();
+                    } else if (keyguardIsShowingTq()) {
+                        if (mLidOpen) {
+                            // If we are opening the lid and not hiding the
+                            // keyguard, then we need to have it turn on the
+                            // screen once it is shown.
+                            mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(
+                                    KeyEvent.KEYCODE_POWER);
+                        }
+                    } else {
+                        // Light up the keyboard if we are sliding up.
+                        if (mLidOpen) {
+                            mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
+                                    LocalPowerManager.BUTTON_EVENT);
+                        } else {
+                            mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
+                                    LocalPowerManager.OTHER_EVENT);
+                        }
+                    }
+                }
+        }
+        return false;
+    }
+
+    
+    /** {@inheritDoc} */
+    public boolean isAppSwitchKeyTqTiLwLi(int keycode) {
+        return keycode == KeyEvent.KEYCODE_HOME
+                || keycode == KeyEvent.KEYCODE_ENDCALL;
+    }
+    
+    /** {@inheritDoc} */
+    public boolean isMovementKeyTi(int keycode) {
+        switch (keycode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * @return Whether a telephone call is in progress right now.
+     */
+    boolean isInCall() {
+        final ITelephony phone = getPhoneInterface();
+        if (phone == null) {
+            Log.w(TAG, "couldn't get ITelephony reference");
+            return false;
+        }
+        try {
+            return phone.isOffhook();
+        } catch (RemoteException e) {
+            Log.w(TAG, "ITelephony.isOffhhook threw RemoteException " + e);
+            return false;
+        }
+    }
+
+    /**
+     * @return Whether music is being played right now.
+     */
+    boolean isMusicActive() {
+        final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+        if (am == null) {
+            Log.w(TAG, "isMusicActive: couldn't get AudioManager reference");
+            return false;
+        }
+        return am.isMusicActive();
+    }
+
+    /**
+     * Tell the audio service to adjust the volume appropriate to the event.
+     * @param keycode
+     */
+    void handleVolumeKey(int stream, int keycode) {
+        final IAudioService audio = getAudioInterface();
+        if (audio == null) {
+            Log.w(TAG, "handleVolumeKey: couldn't get IAudioService reference");
+            return;
+        }
+        try {
+            // since audio is playing, we shouldn't have to hold a wake lock
+            // during the call, but we do it as a precaution for the rare possibility
+            // that the music stops right before we call this
+            mBroadcastWakeLock.acquire();
+            audio.adjustStreamVolume(stream,
+                keycode == KeyEvent.KEYCODE_VOLUME_UP
+                            ? AudioManager.ADJUST_RAISE
+                            : AudioManager.ADJUST_LOWER,
+                    0);
+        } catch (RemoteException e) {
+            Log.w(TAG, "IAudioService.adjustStreamVolume() threw RemoteException " + e);
+        } finally {
+            mBroadcastWakeLock.release();
+        }
+    }
+    
+    static boolean isMediaKey(int code) {
+        if (code == KeyEvent.KEYCODE_HEADSETHOOK || 
+                code == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ||
+                code == KeyEvent.KEYCODE_MEDIA_STOP || 
+                code == KeyEvent.KEYCODE_MEDIA_NEXT ||
+                code == KeyEvent.KEYCODE_MEDIA_PREVIOUS || 
+                code == KeyEvent.KEYCODE_MEDIA_REWIND ||
+                code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
+            return true;
+        }
+        return false;    
+    }
+ 
+    /** {@inheritDoc} */
+    public int interceptKeyTq(RawInputEvent event, boolean screenIsOn) {
+        int result = ACTION_PASS_TO_USER;
+        final boolean isWakeKey = isWakeKeyTq(event);
+        // If screen is off then we treat the case where the keyguard is open but hidden
+        // the same as if it were open and in front.
+        // This will prevent any keys other than the power button from waking the screen
+        // when the keyguard is hidden by another activity.
+        final boolean keyguardActive = (screenIsOn ?
+                                        mKeyguardMediator.isShowingAndNotHidden() :
+                                        mKeyguardMediator.isShowing());
+
+        if (false) {
+            Log.d(TAG, "interceptKeyTq event=" + event + " keycode=" + event.keycode
+                  + " screenIsOn=" + screenIsOn + " keyguardActive=" + keyguardActive);
+        }
+
+        if (keyguardActive) {
+            if (screenIsOn) {
+                // when the screen is on, always give the event to the keyguard
+                result |= ACTION_PASS_TO_USER;
+            } else {
+                // otherwise, don't pass it to the user
+                result &= ~ACTION_PASS_TO_USER;
+
+                final boolean isKeyDown =
+                        (event.type == RawInputEvent.EV_KEY) && (event.value != 0);
+                if (isWakeKey && isKeyDown) {
+
+                    // tell the mediator about a wake key, it may decide to
+                    // turn on the screen depending on whether the key is
+                    // appropriate.
+                    if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode)
+                            && (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN
+                                || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {
+                        // when keyguard is showing and screen off, we need
+                        // to handle the volume key for calls and  music here
+                        if (isInCall()) {
+                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);
+                        } else if (isMusicActive()) {
+                            handleVolumeKey(AudioManager.STREAM_MUSIC, event.keycode);
+                        }
+                    }
+                }
+            }
+        } else if (!screenIsOn) {
+            // If we are in-call with screen off and keyguard is not showing,
+            // then handle the volume key ourselves.
+            // This is necessary because the phone app will disable the keyguard
+            // when the proximity sensor is in use.
+            if (isInCall() && event.type == RawInputEvent.EV_KEY &&
+                     (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN
+                                || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {
+                result &= ~ACTION_PASS_TO_USER;
+                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);
+            }
+            if (isWakeKey) {
+                // a wake key has a sole purpose of waking the device; don't pass
+                // it to the user
+                result |= ACTION_POKE_USER_ACTIVITY;
+                result &= ~ACTION_PASS_TO_USER;
+            }
+        }
+
+        int type = event.type;
+        int code = event.keycode;
+        boolean down = event.value != 0;
+
+        if (type == RawInputEvent.EV_KEY) {
+            if (code == KeyEvent.KEYCODE_ENDCALL
+                    || code == KeyEvent.KEYCODE_POWER) {
+                if (down) {
+                    boolean handled = false;
+                    boolean hungUp = false;
+                    // key repeats are generated by the window manager, and we don't see them
+                    // here, so unless the driver is doing something it shouldn't be, we know
+                    // this is the real press event.
+                    ITelephony phoneServ = getPhoneInterface();
+                    if (phoneServ != null) {
+                        try {
+                            if (code == KeyEvent.KEYCODE_ENDCALL) {
+                                handled = hungUp = phoneServ.endCall();
+                            } else if (code == KeyEvent.KEYCODE_POWER) {
+                                if (phoneServ.isRinging()) {
+                                    // Pressing Power while there's a ringing incoming
+                                    // call should silence the ringer.
+                                    phoneServ.silenceRinger();
+                                    handled = true;
+                                } else if (phoneServ.isOffhook() &&
+                                           ((mIncallPowerBehavior
+                                             & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
+                                            != 0)) {
+                                    // Otherwise, if "Power button ends call" is enabled,
+                                    // the Power button will hang up any current active call.
+                                    handled = hungUp = phoneServ.endCall();
+                                }
+                            }
+                        } catch (RemoteException ex) {
+                            Log.w(TAG, "ITelephony threw RemoteException" + ex);
+                        }
+                    } else {
+                        Log.w(TAG, "!!! Unable to find ITelephony interface !!!");
+                    }
+
+                    if (!screenIsOn
+                            || (handled && code != KeyEvent.KEYCODE_POWER)
+                            || (handled && hungUp && code == KeyEvent.KEYCODE_POWER)) {
+                        mShouldTurnOffOnKeyUp = false;
+                    } else {
+                        // only try to turn off the screen if we didn't already hang up
+                        mShouldTurnOffOnKeyUp = true;
+                        mHandler.postDelayed(mPowerLongPress,
+                                ViewConfiguration.getGlobalActionKeyTimeout());
+                        result &= ~ACTION_PASS_TO_USER;
+                    }
+                } else {
+                    mHandler.removeCallbacks(mPowerLongPress);
+                    if (mShouldTurnOffOnKeyUp) {
+                        mShouldTurnOffOnKeyUp = false;
+                        boolean gohome, sleeps;
+                        if (code == KeyEvent.KEYCODE_ENDCALL) {
+                            gohome = (mEndcallBehavior
+                                      & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;
+                            sleeps = (mEndcallBehavior
+                                      & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;
+                        } else {
+                            gohome = false;
+                            sleeps = true;
+                        }
+                        if (keyguardActive
+                                || (sleeps && !gohome)
+                                || (gohome && !goHome() && sleeps)) {
+                            // they must already be on the keyguad or home screen,
+                            // go to sleep instead
+                            Log.d(TAG, "I'm tired mEndcallBehavior=0x"
+                                    + Integer.toHexString(mEndcallBehavior));
+                            result &= ~ACTION_POKE_USER_ACTIVITY;
+                            result |= ACTION_GO_TO_SLEEP;
+                        }
+                        result &= ~ACTION_PASS_TO_USER;
+                    }
+                }
+            } else if (isMediaKey(code)) {
+                // This key needs to be handled even if the screen is off.
+                // If others need to be handled while it's off, this is a reasonable
+                // pattern to follow.
+                if ((result & ACTION_PASS_TO_USER) == 0) {
+                    // Only do this if we would otherwise not pass it to the user. In that
+                    // case, the PhoneWindow class will do the same thing, except it will
+                    // only do it if the showing app doesn't process the key on its own.
+                    KeyEvent keyEvent = new KeyEvent(event.when, event.when,
+                            down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
+                            code, 0);
+                    mBroadcastWakeLock.acquire();
+                    mHandler.post(new PassHeadsetKey(keyEvent));
+                }
+            } else if (code == KeyEvent.KEYCODE_CALL) {
+                // If an incoming call is ringing, answer it!
+                // (We handle this key here, rather than in the InCallScreen, to make
+                // sure we'll respond to the key even if the InCallScreen hasn't come to
+                // the foreground yet.)
+
+                // We answer the call on the DOWN event, to agree with
+                // the "fallback" behavior in the InCallScreen.
+                if (down) {
+                    try {
+                        ITelephony phoneServ = getPhoneInterface();
+                        if (phoneServ != null) {
+                            if (phoneServ.isRinging()) {
+                                Log.i(TAG, "interceptKeyTq:"
+                                      + " CALL key-down while ringing: Answer the call!");
+                                phoneServ.answerRingingCall();
+
+                                // And *don't* pass this key thru to the current activity
+                                // (which is presumably the InCallScreen.)
+                                result &= ~ACTION_PASS_TO_USER;
+                            }
+                        } else {
+                            Log.w(TAG, "CALL button: Unable to find ITelephony interface");
+                        }
+                    } catch (RemoteException ex) {
+                        Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);
+                    }
+                }
+            } else if ((code == KeyEvent.KEYCODE_VOLUME_UP)
+                       || (code == KeyEvent.KEYCODE_VOLUME_DOWN)) {
+                // If an incoming call is ringing, either VOLUME key means
+                // "silence ringer".  We handle these keys here, rather than
+                // in the InCallScreen, to make sure we'll respond to them
+                // even if the InCallScreen hasn't come to the foreground yet.
+
+                // Look for the DOWN event here, to agree with the "fallback"
+                // behavior in the InCallScreen.
+                if (down) {
+                    try {
+                        ITelephony phoneServ = getPhoneInterface();
+                        if (phoneServ != null) {
+                            if (phoneServ.isRinging()) {
+                                Log.i(TAG, "interceptKeyTq:"
+                                      + " VOLUME key-down while ringing: Silence ringer!");
+                                // Silence the ringer.  (It's safe to call this
+                                // even if the ringer has already been silenced.)
+                                phoneServ.silenceRinger();
+
+                                // And *don't* pass this key thru to the current activity
+                                // (which is probably the InCallScreen.)
+                                result &= ~ACTION_PASS_TO_USER;
+                            }
+                        } else {
+                            Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");
+                        }
+                    } catch (RemoteException ex) {
+                        Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    class PassHeadsetKey implements Runnable {
+        KeyEvent mKeyEvent;
+
+        PassHeadsetKey(KeyEvent keyEvent) {
+            mKeyEvent = keyEvent;
+        }
+
+        public void run() {
+            if (ActivityManagerNative.isSystemReady()) {
+                Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                intent.putExtra(Intent.EXTRA_KEY_EVENT, mKeyEvent);
+                mContext.sendOrderedBroadcast(intent, null, mBroadcastDone,
+                        mHandler, Activity.RESULT_OK, null, null);
+            }
+        }
+    }
+
+    BroadcastReceiver mBroadcastDone = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            mBroadcastWakeLock.release();
+        }
+    };
+
+    BroadcastReceiver mDockReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            try {
+                IUiModeManager uiModeService = IUiModeManager.Stub.asInterface(
+                        ServiceManager.getService(Context.UI_MODE_SERVICE));
+                mUiMode = uiModeService.getCurrentModeType();
+            } catch (RemoteException e) {
+            }
+            updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
+            updateOrientationListenerLp();
+        }
+    };
+
+    /** {@inheritDoc} */
+    public boolean isWakeRelMovementTq(int device, int classes,
+            RawInputEvent event) {
+        // if it's tagged with one of the wake bits, it wakes up the device
+        return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
+    }
+
+    /** {@inheritDoc} */
+    public boolean isWakeAbsMovementTq(int device, int classes,
+            RawInputEvent event) {
+        // if it's tagged with one of the wake bits, it wakes up the device
+        return ((event.flags & (FLAG_WAKE | FLAG_WAKE_DROPPED)) != 0);
+    }
+
+    /**
+     * Given the current state of the world, should this key wake up the device?
+     */
+    protected boolean isWakeKeyTq(RawInputEvent event) {
+        // There are not key maps for trackball devices, but we'd still
+        // like to have pressing it wake the device up, so force it here.
+        int keycode = event.keycode;
+        int flags = event.flags;
+        if (keycode == RawInputEvent.BTN_MOUSE) {
+            flags |= WindowManagerPolicy.FLAG_WAKE;
+        }
+        return (flags
+                & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;
+    }
+
+    /** {@inheritDoc} */
+    public void screenTurnedOff(int why) {
+        EventLog.writeEvent(70000, 0);
+        mKeyguardMediator.onScreenTurnedOff(why);
+        synchronized (mLock) {
+            mScreenOn = false;
+            updateOrientationListenerLp();
+            updateLockScreenTimeout();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void screenTurnedOn() {
+        EventLog.writeEvent(70000, 1);
+        mKeyguardMediator.onScreenTurnedOn();
+        synchronized (mLock) {
+            mScreenOn = true;
+            updateOrientationListenerLp();
+            updateLockScreenTimeout();
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean isScreenOn() {
+        return mScreenOn;
+    }
+    
+    /** {@inheritDoc} */
+    public void enableKeyguard(boolean enabled) {
+        mKeyguardMediator.setKeyguardEnabled(enabled);
+    }
+
+    /** {@inheritDoc} */
+    public void exitKeyguardSecurely(OnKeyguardExitResult callback) {
+        mKeyguardMediator.verifyUnlock(callback);
+    }
+
+    private boolean keyguardIsShowingTq() {
+        return mKeyguardMediator.isShowingAndNotHidden();
+    }
+
+    /** {@inheritDoc} */
+    public boolean inKeyguardRestrictedKeyInputMode() {
+        return mKeyguardMediator.isInputRestricted();
+    }
+
+    void sendCloseSystemWindows() {
+        sendCloseSystemWindows(mContext, null);
+    }
+
+    void sendCloseSystemWindows(String reason) {
+        sendCloseSystemWindows(mContext, reason);
+    }
+
+    static void sendCloseSystemWindows(Context context, String reason) {
+        if (ActivityManagerNative.isSystemReady()) {
+            try {
+                ActivityManagerNative.getDefault().closeSystemDialogs(reason);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public int rotationForOrientationLw(int orientation, int lastRotation,
+            boolean displayEnabled) {
+
+        if (mPortraitRotation < 0) {
+            // Initialize the rotation angles for each orientation once.
+            Display d = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
+                    .getDefaultDisplay();
+            if (d.getWidth() > d.getHeight()) {
+                mPortraitRotation = Surface.ROTATION_90;
+                mLandscapeRotation = Surface.ROTATION_0;
+            } else {
+                mPortraitRotation = Surface.ROTATION_0;
+                mLandscapeRotation = Surface.ROTATION_90;
+            }
+        }
+
+        synchronized (mLock) {
+            switch (orientation) {
+                case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+                    //always return landscape if orientation set to landscape
+                    return mLandscapeRotation;
+                case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+                    //always return portrait if orientation set to portrait
+                    return mPortraitRotation;
+            }
+            // case for nosensor meaning ignore sensor and consider only lid
+            // or orientation sensor disabled
+            //or case.unspecified
+            if (mLidOpen) {
+                return mLidOpenRotation;
+            } else if (mUiMode == Configuration.UI_MODE_TYPE_CAR && mCarDockRotation >= 0) {
+                return mCarDockRotation;
+            } else if (mUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskDockRotation >= 0) {
+                return mDeskDockRotation;
+            } else {
+                if (useSensorForOrientationLp(orientation)) {
+                    // If the user has enabled auto rotation by default, do it.
+                    int curRotation = mOrientationListener.getCurrentRotation();
+                    return curRotation >= 0 ? curRotation : lastRotation;
+                }
+                return Surface.ROTATION_0;
+            }
+        }
+    }
+
+    public boolean detectSafeMode() {
+        try {
+            int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU);
+            int sState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_S);
+            int dpadState = mWindowManager.getDPadKeycodeState(KeyEvent.KEYCODE_DPAD_CENTER);
+            int trackballState = mWindowManager.getTrackballScancodeState(RawInputEvent.BTN_MOUSE);
+            mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0;
+            performHapticFeedbackLw(null, mSafeMode
+                    ? HapticFeedbackConstants.SAFE_MODE_ENABLED
+                    : HapticFeedbackConstants.SAFE_MODE_DISABLED, true);
+            if (mSafeMode) {
+                Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
+                        + " dpad=" + dpadState + " trackball=" + trackballState + ")");
+            } else {
+                Log.i(TAG, "SAFE MODE not enabled");
+            }
+            return mSafeMode;
+        } catch (RemoteException e) {
+            // Doom! (it's also local)
+            throw new RuntimeException("window manager dead");
+        }
+    }
+    
+    static long[] getLongIntArray(Resources r, int resid) {
+        int[] ar = r.getIntArray(resid);
+        if (ar == null) {
+            return null;
+        }
+        long[] out = new long[ar.length];
+        for (int i=0; i<ar.length; i++) {
+            out[i] = ar[i];
+        }
+        return out;
+    }
+    
+    /** {@inheritDoc} */
+    public void systemReady() {
+        // tell the keyguard
+        mKeyguardMediator.onSystemReady();
+        android.os.SystemProperties.set("dev.bootcomplete", "1"); 
+        synchronized (mLock) {
+            updateOrientationListenerLp();
+            mSystemReady = true;
+            mHandler.post(new Runnable() {
+                public void run() {
+                    updateSettings();
+                }
+            });
+        }
+    }
+   
+    /** {@inheritDoc} */
+    public void userActivity() {
+        synchronized (mScreenLockTimeout) {
+            if (mLockScreenTimerActive) {
+                // reset the timer
+                mHandler.removeCallbacks(mScreenLockTimeout);
+                mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
+            }
+        }
+    }
+
+    Runnable mScreenLockTimeout = new Runnable() {
+        public void run() {
+            synchronized (this) {
+                if (localLOGV) Log.v(TAG, "mScreenLockTimeout activating keyguard");
+                mKeyguardMediator.doKeyguardTimeout();
+                mLockScreenTimerActive = false;
+            }
+        }
+    };
+
+    private void updateLockScreenTimeout() {
+        synchronized (mScreenLockTimeout) {
+            boolean enable = (mAllowLockscreenWhenOn && mScreenOn && mKeyguardMediator.isSecure());
+            if (mLockScreenTimerActive != enable) {
+                if (enable) {
+                    if (localLOGV) Log.v(TAG, "setting lockscreen timer");
+                    mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
+                } else {
+                    if (localLOGV) Log.v(TAG, "clearing lockscreen timer");
+                    mHandler.removeCallbacks(mScreenLockTimeout);
+                }
+                mLockScreenTimerActive = enable;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void enableScreenAfterBoot() {
+        readLidState();
+        updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
+    }
+
+    void updateRotation(int animFlags) {
+        mPowerManager.setKeyboardVisibility(mLidOpen);
+        int rotation = Surface.ROTATION_0;
+        if (mLidOpen) {
+            rotation = mLidOpenRotation;
+        } else if (mUiMode == Configuration.UI_MODE_TYPE_CAR && mCarDockRotation >= 0) {
+            rotation = mCarDockRotation;
+        } else if (mUiMode == Configuration.UI_MODE_TYPE_DESK && mDeskDockRotation >= 0) {
+            rotation = mDeskDockRotation;
+        }
+        //if lid is closed orientation will be portrait
+        try {
+            //set orientation on WindowManager
+            mWindowManager.setRotation(rotation, true,
+                    mFancyRotationAnimation | animFlags);
+        } catch (RemoteException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Return an Intent to launch the currently active dock as home.  Returns
+     * null if the standard home should be launched.
+     * @return
+     */
+    Intent createHomeDockIntent() {
+        Intent intent;
+        if (mUiMode == Configuration.UI_MODE_TYPE_CAR) {
+            intent = mCarDockIntent;
+        } else if (mUiMode == Configuration.UI_MODE_TYPE_DESK) {
+            intent = mDeskDockIntent;
+        } else {
+            return null;
+        }
+        
+        ActivityInfo ai = intent.resolveActivityInfo(
+                mContext.getPackageManager(), PackageManager.GET_META_DATA);
+        if (ai == null) {
+            return null;
+        }
+        
+        if (ai.metaData != null && ai.metaData.getBoolean(Intent.METADATA_DOCK_HOME)) {
+            intent = new Intent(intent);
+            intent.setClassName(ai.packageName, ai.name);
+            return intent;
+        }
+        
+        return null;
+    }
+    
+    void startDockOrHome() {
+        Intent dock = createHomeDockIntent();
+        if (dock != null) {
+            try {
+                mContext.startActivity(dock);
+                return;
+            } catch (ActivityNotFoundException e) {
+            }
+        }
+        mContext.startActivity(mHomeIntent);
+    }
+    
+    /**
+     * goes to the home screen
+     * @return whether it did anything
+     */
+    boolean goHome() {
+        if (false) {
+            // This code always brings home to the front.
+            try {
+                ActivityManagerNative.getDefault().stopAppSwitches();
+            } catch (RemoteException e) {
+            }
+            sendCloseSystemWindows();
+            startDockOrHome();
+        } else {
+            // This code brings home to the front or, if it is already
+            // at the front, puts the device to sleep.
+            try {
+                if (SystemProperties.getInt("persist.sys.uts-test-mode", 0) == 1) {
+                    /// Roll back EndcallBehavior as the cupcake design to pass P1 lab entry.
+                    Log.d(TAG, "UTS-TEST-MODE");
+                } else {
+                    ActivityManagerNative.getDefault().stopAppSwitches();
+                    sendCloseSystemWindows();
+                    Intent dock = createHomeDockIntent();
+                    if (dock != null) {
+                        int result = ActivityManagerNative.getDefault()
+                                .startActivity(null, dock,
+                                        dock.resolveTypeIfNeeded(mContext.getContentResolver()),
+                                        null, 0, null, null, 0, true /* onlyIfNeeded*/, false);
+                        if (result == IActivityManager.START_RETURN_INTENT_TO_CALLER) {
+                            return false;
+                        }
+                    }
+                }
+                int result = ActivityManagerNative.getDefault()
+                        .startActivity(null, mHomeIntent,
+                                mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                                null, 0, null, null, 0, true /* onlyIfNeeded*/, false);
+                if (result == IActivityManager.START_RETURN_INTENT_TO_CALLER) {
+                    return false;
+                }
+            } catch (RemoteException ex) {
+                // bummer, the activity manager, which is in this process, is dead
+            }
+        }
+        return true;
+    }
+    
+    public void setCurrentOrientationLw(int newOrientation) {
+        synchronized (mLock) {
+            if (newOrientation != mCurrentAppOrientation) {
+                mCurrentAppOrientation = newOrientation;
+                updateOrientationListenerLp();
+            }
+        }
+    }
+
+    public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
+        final boolean hapticsDisabled = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0;
+        if (!always && (hapticsDisabled || mKeyguardMediator.isShowingAndNotHidden())) {
+            return false;
+        }
+        long[] pattern = null;
+        switch (effectId) {
+            case HapticFeedbackConstants.LONG_PRESS:
+                pattern = mLongPressVibePattern;
+                break;
+            case HapticFeedbackConstants.VIRTUAL_KEY:
+                pattern = mVirtualKeyVibePattern;
+                break;
+            case HapticFeedbackConstants.KEYBOARD_TAP:
+                pattern = mKeyboardTapVibePattern;
+                break;
+            case HapticFeedbackConstants.SAFE_MODE_DISABLED:
+                pattern = mSafeModeDisabledVibePattern;
+                break;
+            case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+                pattern = mSafeModeEnabledVibePattern;
+                break;
+            default:
+                return false;
+        }
+        if (pattern.length == 1) {
+            // One-shot vibration
+            mVibrator.vibrate(pattern[0]);
+        } else {
+            // Pattern vibration
+            mVibrator.vibrate(pattern, -1);
+        }
+        return true;
+    }
+    
+    public void keyFeedbackFromInput(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN
+                && (event.getFlags()&KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0) {
+            performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+        }
+    }
+    
+    public void screenOnStoppedLw() {
+        if (!mKeyguardMediator.isShowingAndNotHidden() && mPowerManager.isScreenOn()) {
+            long curTime = SystemClock.uptimeMillis();
+            mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT);
+        }
+    }
+
+    public boolean allowKeyRepeat() {
+        // disable key repeat when screen is off
+        return mScreenOn;
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/Policy.java b/policy/com/android/internal/policy/impl/Policy.java
new file mode 100644
index 0000000..17f3e91
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/Policy.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.policy.IPolicy;
+import com.android.internal.policy.impl.PhoneLayoutInflater;
+import com.android.internal.policy.impl.PhoneWindow;
+import com.android.internal.policy.impl.PhoneWindowManager;
+
+/**
+ * {@hide}
+ */
+
+// Simple implementation of the policy interface that spawns the right
+// set of objects
+public class Policy implements IPolicy {
+    private static final String TAG = "PhonePolicy";
+
+    private static final String[] preload_classes = {
+        "com.android.internal.policy.impl.PhoneLayoutInflater",
+        "com.android.internal.policy.impl.PhoneWindow",
+        "com.android.internal.policy.impl.PhoneWindow$1",
+        "com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",
+        "com.android.internal.policy.impl.PhoneWindow$DecorView",
+        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",
+        "com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",
+    };
+
+    static {
+        // For performance reasons, preload some policy specific classes when
+        // the policy gets loaded.
+        for (String s : preload_classes) {
+            try {
+                Class.forName(s);
+            } catch (ClassNotFoundException ex) {
+                Log.e(TAG, "Could not preload class for phone policy: " + s);
+            }
+        }
+    }
+
+    public PhoneWindow makeNewWindow(Context context) {
+        return new PhoneWindow(context);
+    }
+
+    public PhoneLayoutInflater makeNewLayoutInflater(Context context) {
+        return new PhoneLayoutInflater(context);
+    }
+
+    public PhoneWindowManager makeNewWindowManager() {
+        return new PhoneWindowManager();
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/PowerDialog.java b/policy/com/android/internal/policy/impl/PowerDialog.java
new file mode 100644
index 0000000..de35bd7
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/PowerDialog.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+import com.android.internal.R;
+
+import android.app.Dialog;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.LocalPowerManager;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+
+import com.android.internal.app.ShutdownThread;
+import com.android.internal.telephony.ITelephony;
+import android.view.KeyEvent;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.widget.Button;
+
+/**
+ * @deprecated use {@link GlobalActions} instead.
+ */
+public class PowerDialog extends Dialog implements OnClickListener,
+        OnKeyListener {
+    private static final String TAG = "PowerDialog";
+
+    static private StatusBarManager sStatusBar;
+    private Button mKeyguard;
+    private Button mPower;
+    private Button mRadioPower;
+    private Button mSilent;
+
+    private LocalPowerManager mPowerManager;
+
+    public PowerDialog(Context context, LocalPowerManager powerManager) {
+        super(context);
+        mPowerManager = powerManager;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Context context = getContext();
+
+        if (sStatusBar == null) {
+            sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
+        }
+
+        setContentView(com.android.internal.R.layout.power_dialog);
+
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        if (!getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_sf_slowBlur)) {
+            getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                    WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+        }
+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+
+        setTitle(context.getText(R.string.power_dialog));
+
+        mKeyguard = (Button) findViewById(R.id.keyguard);
+        mPower = (Button) findViewById(R.id.off);
+        mRadioPower = (Button) findViewById(R.id.radio_power);
+        mSilent = (Button) findViewById(R.id.silent);
+
+        if (mKeyguard != null) {
+            mKeyguard.setOnKeyListener(this);
+            mKeyguard.setOnClickListener(this);
+        }
+        if (mPower != null) {
+            mPower.setOnClickListener(this);
+        }
+        if (mRadioPower != null) {
+            mRadioPower.setOnClickListener(this);
+        }
+        if (mSilent != null) {
+            mSilent.setOnClickListener(this);
+            // XXX: HACK for now hide the silent until we get mute support
+            mSilent.setVisibility(View.GONE);
+        }
+
+        CharSequence text;
+
+        // set the keyguard button's text
+        text = context.getText(R.string.screen_lock);
+        mKeyguard.setText(text);
+        mKeyguard.requestFocus();
+
+        try {
+            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+            if (phone != null) {
+                text = phone.isRadioOn() ? context
+                        .getText(R.string.turn_off_radio) : context
+                        .getText(R.string.turn_on_radio);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        mRadioPower.setText(text);
+    }
+
+    public void onClick(View v) {
+        this.dismiss();
+        if (v == mPower) {
+            // shutdown by making sure radio and power are handled accordingly.
+            ShutdownThread.shutdown(getContext(), true);
+        } else if (v == mRadioPower) {
+            try {
+                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+                if (phone != null) {
+                    phone.toggleRadioOnOff();
+                }
+            } catch (RemoteException ex) {
+                // ignore it
+            }
+        } else if (v == mSilent) {
+            // do something
+        } else if (v == mKeyguard) {
+            if (v.isInTouchMode()) {
+                // only in touch mode for the reasons explained in onKey.
+                this.dismiss();
+                mPowerManager.goToSleep(SystemClock.uptimeMillis() + 1);
+            }
+        }
+    }
+
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        // The activate keyguard button needs to put the device to sleep on the
+        // key up event. If we try to put it to sleep on the click or down
+        // action
+        // the the up action will cause the device to wake back up.
+
+        // Log.i(TAG, "keyCode: " + keyCode + " action: " + event.getAction());
+        if (keyCode != KeyEvent.KEYCODE_DPAD_CENTER
+                || event.getAction() != KeyEvent.ACTION_UP) {
+            // Log.i(TAG, "getting out of dodge...");
+            return false;
+        }
+
+        // Log.i(TAG, "Clicked mKeyguard! dimissing dialog");
+        this.dismiss();
+        // Log.i(TAG, "onKey: turning off the screen...");
+        // XXX: This is a hack for now
+        mPowerManager.goToSleep(event.getEventTime() + 1);
+        return true;
+    }
+
+    public void show() {
+        super.show();
+        Log.d(TAG, "show... disabling expand");
+        sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
+    }
+
+    public void dismiss() {
+        super.dismiss();
+        Log.d(TAG, "dismiss... reenabling expand");
+        sStatusBar.disable(StatusBarManager.DISABLE_NONE);
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/RecentApplicationsBackground.java b/policy/com/android/internal/policy/impl/RecentApplicationsBackground.java
new file mode 100644
index 0000000..7c99e87
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/RecentApplicationsBackground.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A vertical linear layout.  However, instead of drawing the background
+ * behnd the items, it draws the background outside the items based on the
+ * padding.  If there isn't enough room to draw both, it clips the background
+ * instead of the contents.
+ */
+public class RecentApplicationsBackground extends LinearLayout {
+    private static final String TAG = "RecentApplicationsBackground";
+
+    private boolean mBackgroundSizeChanged;
+    private Drawable mBackground;
+    private Rect mTmp0 = new Rect();
+    private Rect mTmp1 = new Rect();
+
+    public RecentApplicationsBackground(Context context) {
+        this(context, null);
+        init();
+    }
+
+    public RecentApplicationsBackground(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    private void init() {
+        mBackground = getBackground();
+        setBackgroundDrawable(null);
+        setPadding(0, 0, 0, 0);
+        setGravity(Gravity.CENTER);
+    }
+
+    @Override
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        setWillNotDraw(false);
+        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
+            mBackgroundSizeChanged = true;
+        }
+        return super.setFrame(left, top, right, bottom);
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return who == mBackground || super.verifyDrawable(who);
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        Drawable d = mBackground;
+        if (d != null && d.isStateful()) {
+            d.setState(getDrawableState());
+        }
+        super.drawableStateChanged();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Drawable background = mBackground;
+        if (background != null) {
+            if (mBackgroundSizeChanged) {
+                mBackgroundSizeChanged = false;
+                Rect chld = mTmp0;
+                Rect bkg = mTmp1;
+                mBackground.getPadding(bkg);
+                getChildBounds(chld);
+                // This doesn't clamp to this view's bounds, which is what we want,
+                // so that the drawing is clipped.
+                final int top = chld.top - bkg.top;
+                final int bottom = chld.bottom + bkg.bottom;
+                // The background here is a gradient that wants to
+                // extend the full width of the screen (whatever that
+                // may be).
+                int left, right;
+                if (false) {
+                    // This limits the width of the drawable.
+                    left = chld.left - bkg.left;
+                    right = chld.right + bkg.right;
+                } else {
+                    // This expands it to full width.
+                    left = 0;
+                    right = getRight();
+                }
+                background.setBounds(left, top, right, bottom);
+            }
+        }
+        mBackground.draw(canvas);
+
+        if (false) {
+            android.graphics.Paint p = new android.graphics.Paint();
+            p.setColor(0x88ffff00);
+            canvas.drawRect(background.getBounds(), p);
+        }
+        canvas.drawARGB((int)(0.75*0xff), 0, 0, 0);
+
+        super.draw(canvas);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mBackground.setCallback(this);
+        setWillNotDraw(false);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mBackground.setCallback(null);
+    }
+    
+    private void getChildBounds(Rect r) {
+        r.left = r.top = Integer.MAX_VALUE;
+        r.bottom = r.right = Integer.MIN_VALUE;
+        final int N = getChildCount();
+        for (int i=0; i<N; i++) {
+            View v = getChildAt(i);
+            if (v.getVisibility() == View.VISIBLE) {
+                r.left = Math.min(r.left, v.getLeft());
+                r.top = Math.min(r.top, v.getTop());
+                r.right = Math.max(r.right, v.getRight());
+                r.bottom = Math.max(r.bottom, v.getBottom());
+            }
+        }
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java
new file mode 100644
index 0000000..8287253
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.app.ActivityManager;
+import android.app.Dialog;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class RecentApplicationsDialog extends Dialog implements OnClickListener {
+    // Elements for debugging support
+//  private static final String LOG_TAG = "RecentApplicationsDialog";
+    private static final boolean DBG_FORCE_EMPTY_LIST = false;
+
+    static private StatusBarManager sStatusBar;
+
+    private static final int NUM_BUTTONS = 8;
+    private static final int MAX_RECENT_TASKS = NUM_BUTTONS * 2;    // allow for some discards
+
+    final TextView[] mIcons = new TextView[NUM_BUTTONS];
+    View mNoAppsText;
+    IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+
+    private int mIconSize;
+
+    public RecentApplicationsDialog(Context context) {
+        super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);
+
+        final Resources resources = context.getResources();
+        mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size);
+    }
+
+    /**
+     * We create the recent applications dialog just once, and it stays around (hidden)
+     * until activated by the user.
+     *
+     * @see PhoneWindowManager#showRecentAppsDialog
+     */
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Context context = getContext();
+
+        if (sStatusBar == null) {
+            sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
+        }
+
+        Window window = getWindow();
+        window.requestFeature(Window.FEATURE_NO_TITLE);
+        window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+        window.setTitle("Recents");
+
+        setContentView(com.android.internal.R.layout.recent_apps_dialog);
+
+        final WindowManager.LayoutParams params = window.getAttributes();
+        params.width = WindowManager.LayoutParams.MATCH_PARENT;
+        params.height = WindowManager.LayoutParams.MATCH_PARENT;
+        window.setAttributes(params);
+        window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+        mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0);
+        mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1);
+        mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2);
+        mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3);
+        mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4);
+        mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5);
+        mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6);
+        mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7);
+        mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);
+
+        for (TextView b: mIcons) {
+            b.setOnClickListener(this);
+        }
+    }
+
+    /**
+     * Handler for user clicks.  If a button was clicked, launch the corresponding activity.
+     */
+    public void onClick(View v) {
+
+        for (TextView b: mIcons) {
+            if (b == v) {
+                // prepare a launch intent and send it
+                Intent intent = (Intent)b.getTag();
+                if (intent != null) {
+                    intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+                    getContext().startActivity(intent);
+                }
+                break;
+            }
+        }
+        dismiss();
+    }
+
+    /**
+     * Set up and show the recent activities dialog.
+     */
+    @Override
+    public void onStart() {
+        super.onStart();
+        reloadButtons();
+        if (sStatusBar != null) {
+            sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
+        }
+
+        // receive broadcasts
+        getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter);
+    }
+
+    /**
+     * Dismiss the recent activities dialog.
+     */
+    @Override
+    public void onStop() {
+        super.onStop();
+
+        // dump extra memory we're hanging on to
+        for (TextView icon: mIcons) {
+            icon.setCompoundDrawables(null, null, null, null);
+            icon.setTag(null);
+        }
+
+        if (sStatusBar != null) {
+            sStatusBar.disable(StatusBarManager.DISABLE_NONE);
+        }
+
+        // stop receiving broadcasts
+        getContext().unregisterReceiver(mBroadcastReceiver);
+     }
+
+    /**
+     * Reload the 6 buttons with recent activities
+     */
+    private void reloadButtons() {
+
+        final Context context = getContext();
+        final PackageManager pm = context.getPackageManager();
+        final ActivityManager am = (ActivityManager)
+                                        context.getSystemService(Context.ACTIVITY_SERVICE);
+        final List<ActivityManager.RecentTaskInfo> recentTasks =
+                                        am.getRecentTasks(MAX_RECENT_TASKS, 0);
+
+        ResolveInfo homeInfo = pm.resolveActivity(
+                new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
+                0);
+
+        IconUtilities iconUtilities = new IconUtilities(getContext());
+
+        // Performance note:  Our android performance guide says to prefer Iterator when
+        // using a List class, but because we know that getRecentTasks() always returns
+        // an ArrayList<>, we'll use a simple index instead.
+        int index = 0;
+        int numTasks = recentTasks.size();
+        for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
+            final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
+
+            // for debug purposes only, disallow first result to create empty lists
+            if (DBG_FORCE_EMPTY_LIST && (i == 0)) continue;
+
+            Intent intent = new Intent(info.baseIntent);
+            if (info.origActivity != null) {
+                intent.setComponent(info.origActivity);
+            }
+
+            // Skip the current home activity.
+            if (homeInfo != null) {
+                if (homeInfo.activityInfo.packageName.equals(
+                        intent.getComponent().getPackageName())
+                        && homeInfo.activityInfo.name.equals(
+                                intent.getComponent().getClassName())) {
+                    continue;
+                }
+            }
+
+            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                    | Intent.FLAG_ACTIVITY_NEW_TASK);
+            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
+            if (resolveInfo != null) {
+                final ActivityInfo activityInfo = resolveInfo.activityInfo;
+                final String title = activityInfo.loadLabel(pm).toString();
+                Drawable icon = activityInfo.loadIcon(pm);
+
+                if (title != null && title.length() > 0 && icon != null) {
+                    final TextView tv = mIcons[index];
+                    tv.setText(title);
+                    icon = iconUtilities.createIconDrawable(icon);
+                    tv.setCompoundDrawables(null, icon, null, null);
+                    tv.setTag(intent);
+                    tv.setVisibility(View.VISIBLE);
+                    tv.setPressed(false);
+                    tv.clearFocus();
+                    ++index;
+                }
+            }
+        }
+
+        // handle the case of "no icons to show"
+        mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);
+
+        // hide the rest
+        for (; index < NUM_BUTTONS; ++index) {
+            mIcons[index].setVisibility(View.GONE);
+        }
+    }
+
+    /**
+     * This is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent.  It's an indication that
+     * we should close ourselves immediately, in order to allow a higher-priority UI to take over
+     * (e.g. phone call received).
+     */
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+                String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
+                if (! PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
+                    dismiss();
+                }
+            }
+        }
+    };
+}
diff --git a/policy/com/android/internal/policy/impl/ShortcutManager.java b/policy/com/android/internal/policy/impl/ShortcutManager.java
new file mode 100644
index 0000000..d86ac44
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/ShortcutManager.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2007 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.policy.impl;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.KeyCharacterMap;
+
+import java.net.URISyntaxException;
+
+/**
+ * Manages quick launch shortcuts by:
+ * <li> Keeping the local copy in sync with the database (this is an observer)
+ * <li> Returning a shortcut-matching intent to clients
+ */
+class ShortcutManager extends ContentObserver {
+    
+    private static final String TAG = "ShortcutManager";
+    
+    private static final int COLUMN_SHORTCUT = 0;
+    private static final int COLUMN_INTENT = 1;
+    private static final String[] sProjection = new String[] {
+        Settings.Bookmarks.SHORTCUT, Settings.Bookmarks.INTENT
+    };
+
+    private Context mContext;
+    private Cursor mCursor;
+    /** Map of a shortcut to its intent. */
+    private SparseArray<Intent> mShortcutIntents;
+    
+    public ShortcutManager(Context context, Handler handler) {
+        super(handler);
+        
+        mContext = context;
+        mShortcutIntents = new SparseArray<Intent>();
+    }
+
+    /** Observes the provider of shortcut+intents */
+    public void observe() {
+        mCursor = mContext.getContentResolver().query(
+                Settings.Bookmarks.CONTENT_URI, sProjection, null, null, null);
+        mCursor.registerContentObserver(this);
+        updateShortcuts();
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        updateShortcuts();
+    }
+    
+    private void updateShortcuts() {
+        Cursor c = mCursor;
+        if (!c.requery()) {
+            Log.e(TAG, "ShortcutObserver could not re-query shortcuts.");
+            return;
+        }
+
+        mShortcutIntents.clear();
+        while (c.moveToNext()) {
+            int shortcut = c.getInt(COLUMN_SHORTCUT);
+            if (shortcut == 0) continue;
+            String intentURI = c.getString(COLUMN_INTENT);
+            Intent intent = null;
+            try {
+                intent = Intent.getIntent(intentURI);
+            } catch (URISyntaxException e) {
+                Log.w(TAG, "Intent URI for shortcut invalid.", e);
+            }
+            if (intent == null) continue;
+            mShortcutIntents.put(shortcut, intent);
+        }
+    }
+    
+    /**
+     * Gets the shortcut intent for a given keycode+modifier. Make sure you
+     * strip whatever modifier is used for invoking shortcuts (for example,
+     * if 'Sym+A' should invoke a shortcut on 'A', you should strip the
+     * 'Sym' bit from the modifiers before calling this method.
+     * <p>
+     * This will first try an exact match (with modifiers), and then try a
+     * match without modifiers (primary character on a key).
+     * 
+     * @param keyCode The keycode of the key pushed.
+     * @param modifiers The modifiers without any that are used for chording
+     *            to invoke a shortcut.
+     * @return The intent that matches the shortcut, or null if not found.
+     */
+    public Intent getIntent(int keyCode, int modifiers) {
+        KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+        // First try the exact keycode (with modifiers)
+        int shortcut = kcm.get(keyCode, modifiers);
+        Intent intent = shortcut != 0 ? mShortcutIntents.get(shortcut) : null; 
+        if (intent != null) return intent;
+        
+        // Next try the keycode without modifiers (the primary character on that key)
+        shortcut = Character.toLowerCase(kcm.get(keyCode, 0));
+        return shortcut != 0 ? mShortcutIntents.get(shortcut) : null;
+    }
+
+}
diff --git a/policy/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/com/android/internal/policy/impl/SimUnlockScreen.java
new file mode 100644
index 0000000..5518e11
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/SimUnlockScreen.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.text.Editable;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.internal.R;
+
+/**
+ * Displays a dialer like interface to unlock the SIM PIN.
+ */
+public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, View.OnClickListener,
+        KeyguardUpdateMonitor.InfoCallback {
+
+    private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardScreenCallback mCallback;
+
+    private TextView mHeaderText;
+    private TextView mPinText;
+
+    private TextView mOkButton;
+    private Button mEmergencyCallButton;
+
+    private View mBackSpaceButton;
+
+    private final int[] mEnteredPin = {0, 0, 0, 0, 0, 0, 0, 0};
+    private int mEnteredDigits = 0;
+
+    private ProgressDialog mSimUnlockProgressDialog = null;
+
+    private LockPatternUtils mLockPatternUtils;
+
+    private int mCreationOrientation;
+
+    private int mKeyboardHidden;
+
+    private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+    public SimUnlockScreen(Context context, Configuration configuration,
+            KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
+            LockPatternUtils lockpatternutils) {
+        super(context);
+        mUpdateMonitor = updateMonitor;
+        mCallback = callback;
+
+        mCreationOrientation = configuration.orientation;
+        mKeyboardHidden = configuration.hardKeyboardHidden;
+        mLockPatternUtils = lockpatternutils;
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
+            inflater.inflate(R.layout.keyguard_screen_sim_pin_landscape, this, true);
+        } else {
+            inflater.inflate(R.layout.keyguard_screen_sim_pin_portrait, this, true);
+            new TouchInput();
+        }
+
+        mHeaderText = (TextView) findViewById(R.id.headerText);
+        mPinText = (TextView) findViewById(R.id.pinDisplay);
+        mBackSpaceButton = findViewById(R.id.backspace);
+        mBackSpaceButton.setOnClickListener(this);
+
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        mOkButton = (TextView) findViewById(R.id.ok);
+
+        mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
+        mPinText.setFocusable(false);
+
+        mEmergencyCallButton.setOnClickListener(this);
+        mOkButton.setOnClickListener(this);
+
+        setFocusableInTouchMode(true);
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        // start fresh
+        mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
+
+        // make sure that the number of entered digits is consistent when we
+        // erase the SIM unlock code, including orientation changes.
+        mPinText.setText("");
+        mEnteredDigits = 0;
+
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        // hide the dialog.
+        if (mSimUnlockProgressDialog != null) {
+            mSimUnlockProgressDialog.hide();
+        }
+        mUpdateMonitor.removeCallback(this);
+    }
+
+
+    /**
+     * Since the IPC can block, we want to run the request in a separate thread
+     * with a callback.
+     */
+    private abstract class CheckSimPin extends Thread {
+
+        private final String mPin;
+
+        protected CheckSimPin(String pin) {
+            mPin = pin;
+        }
+
+        abstract void onSimLockChangedResponse(boolean success);
+
+        @Override
+        public void run() {
+            try {
+                final boolean result = ITelephony.Stub.asInterface(ServiceManager
+                        .checkService("phone")).supplyPin(mPin);
+                post(new Runnable() {
+                    public void run() {
+                        onSimLockChangedResponse(result);
+                    }
+                });
+            } catch (RemoteException e) {
+                post(new Runnable() {
+                    public void run() {
+                        onSimLockChangedResponse(false);
+                    }
+                });
+            }
+        }
+    }
+
+    public void onClick(View v) {
+        if (v == mBackSpaceButton) {
+            final Editable digits = mPinText.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+                mEnteredDigits--;
+            }
+            mCallback.pokeWakelock();
+        } else if (v == mEmergencyCallButton) {
+            mCallback.takeEmergencyCallAction();
+        } else if (v == mOkButton) {
+            checkPin();
+        }
+    }
+
+    private Dialog getSimUnlockProgressDialog() {
+        if (mSimUnlockProgressDialog == null) {
+            mSimUnlockProgressDialog = new ProgressDialog(mContext);
+            mSimUnlockProgressDialog.setMessage(
+                    mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message));
+            mSimUnlockProgressDialog.setIndeterminate(true);
+            mSimUnlockProgressDialog.setCancelable(false);
+            mSimUnlockProgressDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_sf_slowBlur)) {
+                mSimUnlockProgressDialog.getWindow().setFlags(
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+            }
+        }
+        return mSimUnlockProgressDialog;
+    }
+
+    private void checkPin() {
+
+        // make sure that the pin is at least 4 digits long.
+        if (mEnteredDigits < 4) {
+            // otherwise, display a message to the user, and don't submit.
+            mHeaderText.setText(R.string.invalidPin);
+            mPinText.setText("");
+            mEnteredDigits = 0;
+            mCallback.pokeWakelock();
+            return;
+        }
+        getSimUnlockProgressDialog().show();
+
+        new CheckSimPin(mPinText.getText().toString()) {
+            void onSimLockChangedResponse(boolean success) {
+                if (mSimUnlockProgressDialog != null) {
+                    mSimUnlockProgressDialog.hide();
+                }
+                if (success) {
+                    // before closing the keyguard, report back that
+                    // the sim is unlocked so it knows right away
+                    mUpdateMonitor.reportSimPinUnlocked();
+                    mCallback.goToUnlockScreen();
+                } else {
+                    mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
+                    mPinText.setText("");
+                    mEnteredDigits = 0;
+                }
+                mCallback.pokeWakelock();
+            }
+        }.start();
+    }
+
+
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            mCallback.goToLockScreen();
+            return true;
+        }
+
+        final char match = event.getMatch(DIGITS);
+        if (match != 0) {
+            reportDigit(match - '0');
+            return true;
+        }
+        if (keyCode == KeyEvent.KEYCODE_DEL) {
+            if (mEnteredDigits > 0) {
+                mPinText.onKeyDown(keyCode, event);
+                mEnteredDigits--;
+            }
+            return true;
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_ENTER) {
+            checkPin();
+            return true;
+        }
+
+        return false;
+    }
+
+    private void reportDigit(int digit) {
+        if (mEnteredDigits == 0) {
+            mPinText.setText("");
+        }
+        if (mEnteredDigits == 8) {
+            return;
+        }
+        mPinText.append(Integer.toString(digit));
+        mEnteredPin[mEnteredDigits++] = digit;
+    }
+
+    void updateConfiguration() {
+        Configuration newConfig = getResources().getConfiguration();
+        if (newConfig.orientation != mCreationOrientation) {
+            mCallback.recreateMe(newConfig);
+        } else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
+            mKeyboardHidden = newConfig.hardKeyboardHidden;
+            final boolean isKeyboardOpen = mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO;
+            if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
+                mCallback.goToUnlockScreen();
+            }
+        }
+        
+    }
+    
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateConfiguration();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateConfiguration();
+    }
+
+    /**
+     * Helper class to handle input from touch dialer.  Only relevant when
+     * the keyboard is shut.
+     */
+    private class TouchInput implements View.OnClickListener {
+        private TextView mZero;
+        private TextView mOne;
+        private TextView mTwo;
+        private TextView mThree;
+        private TextView mFour;
+        private TextView mFive;
+        private TextView mSix;
+        private TextView mSeven;
+        private TextView mEight;
+        private TextView mNine;
+        private TextView mCancelButton;
+
+        private TouchInput() {
+            mZero = (TextView) findViewById(R.id.zero);
+            mOne = (TextView) findViewById(R.id.one);
+            mTwo = (TextView) findViewById(R.id.two);
+            mThree = (TextView) findViewById(R.id.three);
+            mFour = (TextView) findViewById(R.id.four);
+            mFive = (TextView) findViewById(R.id.five);
+            mSix = (TextView) findViewById(R.id.six);
+            mSeven = (TextView) findViewById(R.id.seven);
+            mEight = (TextView) findViewById(R.id.eight);
+            mNine = (TextView) findViewById(R.id.nine);
+            mCancelButton = (TextView) findViewById(R.id.cancel);
+
+            mZero.setText("0");
+            mOne.setText("1");
+            mTwo.setText("2");
+            mThree.setText("3");
+            mFour.setText("4");
+            mFive.setText("5");
+            mSix.setText("6");
+            mSeven.setText("7");
+            mEight.setText("8");
+            mNine.setText("9");
+
+            mZero.setOnClickListener(this);
+            mOne.setOnClickListener(this);
+            mTwo.setOnClickListener(this);
+            mThree.setOnClickListener(this);
+            mFour.setOnClickListener(this);
+            mFive.setOnClickListener(this);
+            mSix.setOnClickListener(this);
+            mSeven.setOnClickListener(this);
+            mEight.setOnClickListener(this);
+            mNine.setOnClickListener(this);
+            mCancelButton.setOnClickListener(this);
+        }
+
+
+        public void onClick(View v) {
+            if (v == mCancelButton) {
+                mCallback.goToLockScreen();
+                return;
+            }
+
+            final int digit = checkDigit(v);
+            if (digit >= 0) {
+                mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS);
+                reportDigit(digit);
+            }
+        }
+
+        private int checkDigit(View v) {
+            int digit = -1;
+            if (v == mZero) {
+                digit = 0;
+            } else if (v == mOne) {
+                digit = 1;
+            } else if (v == mTwo) {
+                digit = 2;
+            } else if (v == mThree) {
+                digit = 3;
+            } else if (v == mFour) {
+                digit = 4;
+            } else if (v == mFive) {
+                digit = 5;
+            } else if (v == mSix) {
+                digit = 6;
+            } else if (v == mSeven) {
+                digit = 7;
+            } else if (v == mEight) {
+                digit = 8;
+            } else if (v == mNine) {
+                digit = 9;
+            }
+            return digit;
+        }
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+    }
+
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+
+    }
+
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+
+    }
+
+    public void onRingerModeChanged(int state) {
+
+    }
+
+    public void onTimeChanged() {
+
+    }
+}
diff --git a/policy/com/android/internal/policy/impl/package.html b/policy/com/android/internal/policy/impl/package.html
new file mode 100644
index 0000000..c9f96a6
--- /dev/null
+++ b/policy/com/android/internal/policy/impl/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/preloaded-classes b/preloaded-classes
index 54c7303..5d2fd68 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -269,7 +269,6 @@
 android.location.ILocationManager$Stub$Proxy
 android.location.Location
 android.location.LocationManager
-android.location.LocationProviderInterface
 android.media.AudioFormat
 android.media.AudioManager
 android.media.AudioRecord
@@ -633,7 +632,6 @@
 com.android.internal.content.SyncStateContentProviderHelper
 com.android.internal.graphics.NativeUtils
 com.android.internal.location.DummyLocationProvider
-com.android.internal.location.GpsLocationProvider
 com.android.internal.logging.AndroidHandler
 com.android.internal.os.AndroidPrintStream
 com.android.internal.os.BinderInternal
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 91dfaf3..a074023 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -421,7 +421,7 @@
                 Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
         // If Encrypted file systems is enabled or disabled, this call will return the
         // correct directory.
-        mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
+        mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
         mBaseStateDir.mkdirs();
         mDataDir = Environment.getDownloadCacheDirectory();
 
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 2eaa58c..1f34eba 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -166,11 +166,17 @@
 		}
 	}
 
-    public int install(String name, int uid, int gid) {
+    public int install(String name, boolean useEncryptedFilesystem, int uid, int gid) {
         StringBuilder builder = new StringBuilder("install");
         builder.append(' ');
         builder.append(name);
         builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
+        builder.append(' ');
         builder.append(uid);
         builder.append(' ');
         builder.append(gid);
@@ -203,33 +209,57 @@
         return execute(builder.toString());
     }
 
-    public int remove(String name) {
+    public int remove(String name, boolean useEncryptedFilesystem) {
         StringBuilder builder = new StringBuilder("remove");
         builder.append(' ');
         builder.append(name);
+        builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
         return execute(builder.toString());
     }
 
-    public int rename(String oldname, String newname) {
+    public int rename(String oldname, String newname, boolean useEncryptedFilesystem) {
         StringBuilder builder = new StringBuilder("rename");
         builder.append(' ');
         builder.append(oldname);
         builder.append(' ');
         builder.append(newname);
+        builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
         return execute(builder.toString());
     }
 
-    public int deleteCacheFiles(String name) {
+    public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) {
         StringBuilder builder = new StringBuilder("rmcache");
         builder.append(' ');
         builder.append(name);
+        builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
         return execute(builder.toString());
     }
     
-    public int clearUserData(String name) {
+    public int clearUserData(String name, boolean useEncryptedFilesystem) {
         StringBuilder builder = new StringBuilder("rmuserdata");
         builder.append(' ');
         builder.append(name);
+        builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
         return execute(builder.toString());
     }
     
@@ -263,7 +293,7 @@
     }
     
     public int getSizeInfo(String pkgName, String apkPath,
-            String fwdLockApkPath, PackageStats pStats) {
+            String fwdLockApkPath, PackageStats pStats, boolean useEncryptedFilesystem) {
         StringBuilder builder = new StringBuilder("getsize");
         builder.append(' ');
         builder.append(pkgName);
@@ -271,6 +301,13 @@
         builder.append(apkPath);
         builder.append(' ');
         builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
+        builder.append(' ');
+        if (useEncryptedFilesystem) {
+            builder.append('1');
+        } else {
+            builder.append('0');
+        }
+
         String s = transaction(builder.toString());
         String res[] = s.split(" ");
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index ef57056..f9c1a93 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,17 +16,6 @@
 
 package com.android.server;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Observable;
-import java.util.Observer;
-import java.util.Set;
-
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -50,7 +39,6 @@
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -68,12 +56,25 @@
 import android.util.Slog;
 import android.util.PrintWriterPrinter;
 
-import com.android.internal.location.GeocoderProxy;
-import com.android.internal.location.GpsLocationProvider;
 import com.android.internal.location.GpsNetInitiatedHandler;
-import com.android.internal.location.LocationProviderProxy;
-import com.android.internal.location.MockProvider;
-import com.android.internal.location.PassiveProvider;
+
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Set;
 
 /**
  * The service class that manages LocationProviders and issues location
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 27a8a74..4d04cee 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -39,7 +39,11 @@
 
         try {
             Slog.w(TAG, "!!! FACTORY RESET !!!");
-            RecoverySystem.rebootWipeUserData(context);
+            if (intent.hasExtra("enableEFS")) {
+                RecoverySystem.rebootToggleEFS(context, intent.getBooleanExtra("enableEFS", false));
+            } else {
+                RecoverySystem.rebootWipeUserData(context);
+            }
             Log.wtf(TAG, "Still running after master clear?!");
         } catch (IOException e) {
             Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index e6663d4..0f89401 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -149,6 +149,8 @@
 
     private static final boolean GET_CERTIFICATES = true;
 
+    private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
+
     private static final int REMOVE_EVENTS =
         FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
     private static final int ADD_EVENTS =
@@ -201,6 +203,10 @@
     // This is where all application persistent data goes.
     final File mAppDataDir;
 
+    // If Encrypted File System feature is enabled, all application persistent data
+    // should go here instead.
+    final File mSecureAppDataDir;
+
     // This is the object monitoring the framework dir.
     final FileObserver mFrameworkInstallObserver;
 
@@ -748,6 +754,7 @@
 
             File dataDir = Environment.getDataDirectory();
             mAppDataDir = new File(dataDir, "data");
+            mSecureAppDataDir = new File(dataDir, "secure/data");
             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
 
             if (mInstaller == null) {
@@ -757,6 +764,7 @@
                 File miscDir = new File(dataDir, "misc");
                 miscDir.mkdirs();
                 mAppDataDir.mkdirs();
+                mSecureAppDataDir.mkdirs();
                 mDrmAppPrivateInstallDir.mkdirs();
             }
 
@@ -917,7 +925,9 @@
                             + " no longer exists; wiping its data";
                     reportSettingsProblem(Log.WARN, msg);
                     if (mInstaller != null) {
-                        mInstaller.remove(ps.name);
+                        // XXX how to set useEncryptedFSDir for packages that
+                        // are not encrypted?
+                        mInstaller.remove(ps.name, true);
                     }
                 }
             }
@@ -1000,7 +1010,8 @@
     void cleanupInstallFailedPackage(PackageSetting ps) {
         Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
         if (mInstaller != null) {
-            int retCode = mInstaller.remove(ps.name);
+            boolean useSecureFS = useEncryptedFilesystemForPackage(ps.pkg);
+            int retCode = mInstaller.remove(ps.name, useSecureFS);
             if (retCode < 0) {
                 Slog.w(TAG, "Couldn't remove app data directory for package: "
                            + ps.name + ", retcode=" + retCode);
@@ -2713,6 +2724,11 @@
 
         return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
     }
+
+    private static boolean useEncryptedFilesystemForPackage(PackageParser.Package pkg) {
+        return Environment.isEncryptedFilesystemEnabled() &&
+                ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_NEVER_ENCRYPT) == 0);
+    }
     
     private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
         if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -2730,7 +2746,14 @@
     }
 
     private File getDataPathForPackage(PackageParser.Package pkg) {
-        return new File(mAppDataDir, pkg.packageName);
+        boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
+        File dataPath;
+        if (useEncryptedFSDir) {
+            dataPath = new File(mSecureAppDataDir, pkg.packageName);
+        } else {
+            dataPath = new File(mAppDataDir, pkg.packageName);
+        }
+        return dataPath;
     }
     
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
@@ -3081,6 +3104,7 @@
             pkg.applicationInfo.dataDir = dataPath.getPath();
         } else {
             // This is a normal package, need to make its data directory.
+            boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(pkg);
             dataPath = getDataPathForPackage(pkg);
             
             boolean uidError = false;
@@ -3097,7 +3121,7 @@
                         // If this is a system app, we can at least delete its
                         // current data so the application will still work.
                         if (mInstaller != null) {
-                            int ret = mInstaller.remove(pkgName);
+                            int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
                             if (ret >= 0) {
                                 // Old data gone!
                                 String msg = "System package " + pkg.packageName
@@ -3108,7 +3132,7 @@
                                 recovered = true;
 
                                 // And now re-install the app.
-                                ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+                                ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
                                         pkg.applicationInfo.uid);
                                 if (ret == -1) {
                                     // Ack should not happen!
@@ -3148,7 +3172,7 @@
                     Log.v(TAG, "Want this data dir: " + dataPath);
                 //invoke installer to do the actual installation
                 if (mInstaller != null) {
-                    int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
+                    int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
                             pkg.applicationInfo.uid);
                     if(ret < 0) {
                         // Error from installer
@@ -6115,8 +6139,9 @@
             deletedPs = mSettings.mPackages.get(packageName);
         }
         if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
+            boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
             if (mInstaller != null) {
-                int retCode = mInstaller.remove(packageName);
+                int retCode = mInstaller.remove(packageName, useEncryptedFSDir);
                 if (retCode < 0) {
                     Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
                                + packageName + ", retcode=" + retCode);
@@ -6355,6 +6380,7 @@
                 p = ps.pkg;
             }
         }
+        boolean useEncryptedFSDir = false;
 
         if(!dataOnly) {
             //need to check this only for fully installed applications
@@ -6367,9 +6393,10 @@
                 Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
                 return false;
             }
+            useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
         }
         if (mInstaller != null) {
-            int retCode = mInstaller.clearUserData(packageName);
+            int retCode = mInstaller.clearUserData(packageName, useEncryptedFSDir);
             if (retCode < 0) {
                 Slog.w(TAG, "Couldn't remove cache files for package: "
                         + packageName);
@@ -6420,8 +6447,9 @@
             Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
             return false;
         }
+        boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
         if (mInstaller != null) {
-            int retCode = mInstaller.deleteCacheFiles(packageName);
+            int retCode = mInstaller.deleteCacheFiles(packageName, useEncryptedFSDir);
             if (retCode < 0) {
                 Slog.w(TAG, "Couldn't remove cache files for package: "
                            + packageName);
@@ -6483,9 +6511,10 @@
             }
             publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
         }
+        boolean useEncryptedFSDir = useEncryptedFilesystemForPackage(p);
         if (mInstaller != null) {
             int res = mInstaller.getSizeInfo(packageName, p.mPath,
-                    publicSrcDir, pStats);
+                    publicSrcDir, pStats, useEncryptedFSDir);
             if (res < 0) {
                 return false;
             } else {
@@ -7535,7 +7564,8 @@
             this.pkgFlags = pkgFlags & (
                     ApplicationInfo.FLAG_SYSTEM |
                     ApplicationInfo.FLAG_FORWARD_LOCK |
-                    ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+                    ApplicationInfo.FLAG_EXTERNAL_STORAGE |
+                    ApplicationInfo.FLAG_NEVER_ENCRYPT);
         }
     }
 
@@ -7802,11 +7832,17 @@
             File dataDir = Environment.getDataDirectory();
             File systemDir = new File(dataDir, "system");
             // TODO(oam): This secure dir creation needs to be moved somewhere else (later)
+            File systemSecureDir = new File(dataDir, "secure/system");
             systemDir.mkdirs();
+            systemSecureDir.mkdirs();
             FileUtils.setPermissions(systemDir.toString(),
                     FileUtils.S_IRWXU|FileUtils.S_IRWXG
                     |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                     -1, -1);
+            FileUtils.setPermissions(systemSecureDir.toString(),
+                    FileUtils.S_IRWXU|FileUtils.S_IRWXG
+                    |FileUtils.S_IROTH|FileUtils.S_IXOTH,
+                    -1, -1);
             mSettingsFilename = new File(systemDir, "packages.xml");
             mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
             mPackageListFilename = new File(systemDir, "packages.list");
diff --git a/location/java/com/android/internal/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
similarity index 98%
rename from location/java/com/android/internal/location/GeocoderProxy.java
rename to services/java/com/android/server/location/GeocoderProxy.java
index b06297b..3c05da2 100644
--- a/location/java/com/android/internal/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package com.android.server.location;
 
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
similarity index 96%
rename from location/java/com/android/internal/location/GpsLocationProvider.java
rename to services/java/com/android/server/location/GpsLocationProvider.java
index 15d692c..daa198f 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package com.android.server.location;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -30,7 +30,6 @@
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.SntpClient;
@@ -77,37 +76,6 @@
     private static final boolean DEBUG = false;
     private static final boolean VERBOSE = false;
 
-    /**
-     * Broadcast intent action indicating that the GPS has either been
-     * enabled or disabled. An intent extra provides this state as a boolean,
-     * where {@code true} means enabled.
-     * @see #EXTRA_ENABLED
-     *
-     * {@hide}
-     */
-    public static final String GPS_ENABLED_CHANGE_ACTION =
-        "android.location.GPS_ENABLED_CHANGE";
-
-    /**
-     * Broadcast intent action indicating that the GPS has either started or
-     * stopped receiving GPS fixes. An intent extra provides this state as a
-     * boolean, where {@code true} means that the GPS is actively receiving fixes.
-     * @see #EXTRA_ENABLED
-     *
-     * {@hide}
-     */
-    public static final String GPS_FIX_CHANGE_ACTION =
-        "android.location.GPS_FIX_CHANGE";
-
-    /**
-     * The lookup key for a boolean that indicates whether GPS is enabled or
-     * disabled. {@code true} means GPS is enabled. Retrieve it with
-     * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
-     *
-     * {@hide}
-     */
-    public static final String EXTRA_ENABLED = "enabled";
-
     // these need to match GpsPositionMode enum in gps.h
     private static final int GPS_POSITION_MODE_STANDALONE = 0;
     private static final int GPS_POSITION_MODE_MS_BASED = 1;
@@ -348,7 +316,7 @@
     public GpsLocationProvider(Context context, ILocationManager locationManager) {
         mContext = context;
         mLocationManager = locationManager;
-        mNIHandler = new GpsNetInitiatedHandler(context, this);
+        mNIHandler = new GpsNetInitiatedHandler(context);
 
         mLocation.setExtras(mLocationExtras);
 
@@ -1031,8 +999,8 @@
             }
 
             // send an intent to notify that the GPS is receiving fixes.
-            Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
-            intent.putExtra(EXTRA_ENABLED, true);
+            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
             mContext.sendBroadcast(intent);
             updateStatus(LocationProvider.AVAILABLE, mSvCount);
         }
@@ -1108,8 +1076,8 @@
                 }
 
                 // send an intent to notify that the GPS has been enabled or disabled.
-                Intent intent = new Intent(GPS_ENABLED_CHANGE_ACTION);
-                intent.putExtra(EXTRA_ENABLED, mNavigating);
+                Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+                intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
                 mContext.sendBroadcast(intent);
             }
 
@@ -1165,8 +1133,8 @@
         if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
             System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT * 1000) {
             // send an intent to notify that the GPS is no longer receiving fixes.
-            Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
-            intent.putExtra(EXTRA_ENABLED, false);
+            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
+            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
             mContext.sendBroadcast(intent);
             updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
         }
diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/services/java/com/android/server/location/GpsXtraDownloader.java
similarity index 98%
rename from location/java/com/android/internal/location/GpsXtraDownloader.java
rename to services/java/com/android/server/location/GpsXtraDownloader.java
index 978bda2..bc96980 100644
--- a/location/java/com/android/internal/location/GpsXtraDownloader.java
+++ b/services/java/com/android/server/location/GpsXtraDownloader.java
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package com.android.server.location;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.net.http.AndroidHttpClient;
+import android.util.Config;
+import android.util.Log;
 
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
@@ -30,14 +36,6 @@
 import java.util.Properties;
 import java.util.Random;
 
-import android.content.Context;
-import android.net.Proxy;
-import android.net.http.AndroidHttpClient;
-import android.util.Config;
-import android.util.Log;
-
-
-
 /**
  * A class for downloading GPS XTRA data.
  *
diff --git a/location/java/android/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
similarity index 97%
rename from location/java/android/location/LocationProviderInterface.java
rename to services/java/com/android/server/location/LocationProviderInterface.java
index 5ffe15c..a472143 100644
--- a/location/java/android/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.location;
+package com.android.server.location;
 
 import android.location.Location;
 import android.net.NetworkInfo;
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
similarity index 98%
rename from location/java/com/android/internal/location/LocationProviderProxy.java
rename to services/java/com/android/server/location/LocationProviderProxy.java
index 31ec09a..3e118f9 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package com.android.server.location;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -22,7 +22,6 @@
 import android.content.ServiceConnection;
 import android.location.ILocationProvider;
 import android.location.Location;
-import android.location.LocationProviderInterface;
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.Handler;
@@ -31,6 +30,8 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import com.android.internal.location.DummyLocationProvider;
+
 /**
  * A class for proxying location providers implemented as services.
  *
diff --git a/location/java/com/android/internal/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
similarity index 97%
rename from location/java/com/android/internal/location/MockProvider.java
rename to services/java/com/android/server/location/MockProvider.java
index d912740..e3f33469 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -14,12 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package com.android.server.location;
 
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
diff --git a/location/java/com/android/internal/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
similarity index 96%
rename from location/java/com/android/internal/location/PassiveProvider.java
rename to services/java/com/android/server/location/PassiveProvider.java
index ab90937..5ed1558 100644
--- a/location/java/com/android/internal/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package com.android.server.location;
 
 import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
-import android.location.LocationProviderInterface;
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 465ff2e..c9b649d 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -30,6 +30,7 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
@@ -62,7 +63,6 @@
 
 import com.android.internal.R;
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.location.GpsLocationProvider;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.EriInfo;
@@ -387,8 +387,8 @@
                     action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
                 updateWifi(intent);
             }
-            else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) ||
-                    action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION)) {
+            else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
+                    action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
                 updateGps(intent);
             }
             else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
@@ -533,8 +533,8 @@
         filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-        filter.addAction(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION);
-        filter.addAction(GpsLocationProvider.GPS_FIX_CHANGE_ACTION);
+        filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+        filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
@@ -1287,13 +1287,13 @@
 
     private final void updateGps(Intent intent) {
         final String action = intent.getAction();
-        final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED, false);
+        final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
 
-        if (action.equals(GpsLocationProvider.GPS_FIX_CHANGE_ACTION) && enabled) {
+        if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
             // GPS is getting fixes
             mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
             mService.setIconVisibility(mGpsIcon, true);
-        } else if (action.equals(GpsLocationProvider.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
+        } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
             // GPS is off
             mService.setIconVisibility(mGpsIcon, false);
         } else {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index 9d2760e..b90e327 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -9,6 +9,7 @@
     com_android_server_SensorService.cpp \
     com_android_server_SystemServer.cpp \
     com_android_server_VibratorService.cpp \
+	com_android_server_location_GpsLocationProvider.cpp \
     onload.cpp
 
 LOCAL_C_INCLUDES += \
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
similarity index 93%
rename from core/jni/android_location_GpsLocationProvider.cpp
rename to services/jni/com_android_server_location_GpsLocationProvider.cpp
index f60fe6d..7a74fd4 100755
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -20,8 +20,8 @@
 
 #include "JNIHelp.h"
 #include "jni.h"
-#include "hardware_legacy/gps.h"
-#include "hardware_legacy/gps_ni.h"
+#include "hardware/hardware.h"
+#include "hardware/gps.h"
 #include "utils/Log.h"
 #include "utils/misc.h"
 
@@ -41,7 +41,6 @@
 static const GpsInterface* sGpsInterface = NULL;
 static const GpsXtraInterface* sGpsXtraInterface = NULL;
 static const AGpsInterface* sAGpsInterface = NULL;
-static const GpsPrivacyInterface* sGpsPrivacyInterface = NULL;
 static const GpsNiInterface* sGpsNiInterface = NULL;
 static const GpsDebugInterface* sGpsDebugInterface = NULL;
 
@@ -205,16 +204,34 @@
     method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
 }
 
+static const GpsInterface* get_gps_interface() {
+    int err;
+    hw_module_t* module;
+    const GpsInterface* interface = NULL;
+
+    err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
+    if (err == 0) {
+        hw_device_t* device;
+        err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
+        if (err == 0) {
+            gps_device_t* gps_device = (gps_device_t *)device;
+            interface = gps_device->get_gps_interface(gps_device);
+        }
+    }
+
+    return interface;
+}
+
 static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
     if (!sGpsInterface)
-        sGpsInterface = gps_get_interface();
+        sGpsInterface = get_gps_interface();
     return (sGpsInterface != NULL);
 }
 
 static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
 {
     if (!sGpsInterface)
-        sGpsInterface = gps_get_interface();
+        sGpsInterface = get_gps_interface();
     if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
         return false;
 
@@ -224,15 +241,9 @@
         sAGpsInterface->init(&sAGpsCallbacks);
 
     if (!sGpsNiInterface)
-        sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+       sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
     if (sGpsNiInterface)
-        sGpsNiInterface->init(&sGpsNiCallbacks);
-
-    // Clear privacy lock while enabled
-    if (!sGpsPrivacyInterface)
-        sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE);
-    if (sGpsPrivacyInterface)
-        sGpsPrivacyInterface->set_privacy_lock(0);
+       sGpsNiInterface->init(&sGpsNiCallbacks);
 
     if (!sGpsDebugInterface)
        sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
@@ -242,12 +253,6 @@
 
 static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj)
 {
-    // Enable privacy lock while disabled
-    if (!sGpsPrivacyInterface)
-        sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE);
-    if (sGpsPrivacyInterface)
-        sGpsPrivacyInterface->set_privacy_lock(1);
-
     pthread_mutex_lock(&sEventMutex);
     sPendingCallbacks |= kDisableRequest;
     pthread_cond_signal(&sEventCond);
@@ -489,10 +494,12 @@
 static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
       jint notifId, jint response)
 {
-    if (!sGpsNiInterface)
+    if (!sGpsNiInterface) {
         sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
-    if (sGpsNiInterface)
+    }
+    if (sGpsNiInterface) {
         sGpsNiInterface->respond(notifId, response);
+    }
 }
 
 static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj)
@@ -534,9 +541,9 @@
     {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
 };
 
-int register_android_location_GpsLocationProvider(JNIEnv* env)
+int register_android_server_location_GpsLocationProvider(JNIEnv* env)
 {
-    return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods));
+    return jniRegisterNativeMethods(env, "com/android/server/location/GpsLocationProvider", sMethods, NELEM(sMethods));
 }
 
 } /* namespace android */
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index c16fdb8..d11e7e1 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -11,6 +11,7 @@
 int register_android_server_SensorService(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
+int register_android_server_location_GpsLocationProvider(JNIEnv* env);
 };
 
 using namespace android;
@@ -33,6 +34,7 @@
     register_android_server_SensorService(env);
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
+    register_android_server_location_GpsLocationProvider(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/tests/framework-tests/src/Android.mk b/tests/framework-tests/src/Android.mk
index 54e33a4..f537b52 100644
--- a/tests/framework-tests/src/Android.mk
+++ b/tests/framework-tests/src/Android.mk
@@ -5,6 +5,6 @@
 
 LOCAL_MODULE := framework-tests
 
-LOCAL_JAVA_LIBRARIES := android.policy_phone android.test.runner
+LOCAL_JAVA_LIBRARIES := android.policy android.test.runner
 
 include $(BUILD_JAVA_LIBRARY)
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index ce522c8..ce40b5d 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -85,7 +85,8 @@
 
     // TODO(oam): Test VPN when EFS is enabled (will do later)...
     public static String getProfilePath() {
-        return Environment.getDataDirectory().getPath() + PROFILES_PATH;
+        // This call will return the correct path if Encrypted FS is enabled or not.
+        return Environment.getSecureDataDirectory().getPath() + PROFILES_PATH;
     }
 
     /**