Merge change 6144

* changes:
  Fix the performance collection in the http thread. A connection can be reused. Change the thread time collection based on per request.
diff --git a/api/current.xml b/api/current.xml
index c92b018..c93f706 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3485,50 +3485,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad22"
- type="int"
- transient="false"
- volatile="false"
- value="16843402"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad23"
- type="int"
- transient="false"
- volatile="false"
- value="16843401"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad24"
- type="int"
- transient="false"
- volatile="false"
- value="16843400"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad25"
- type="int"
- transient="false"
- volatile="false"
- value="16843399"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad3"
  type="int"
  transient="false"
@@ -6807,6 +6763,17 @@
  visibility="public"
 >
 </field>
+<field name="progressBarStyleInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843399"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progressBarStyleLarge"
  type="int"
  transient="false"
@@ -6818,6 +6785,17 @@
  visibility="public"
 >
 </field>
+<field name="progressBarStyleLargeInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843401"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progressBarStyleSmall"
  type="int"
  transient="false"
@@ -6829,6 +6807,17 @@
  visibility="public"
 >
 </field>
+<field name="progressBarStyleSmallInverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843400"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="progressBarStyleSmallTitle"
  type="int"
  transient="false"
@@ -7379,6 +7368,17 @@
  visibility="public"
 >
 </field>
+<field name="searchSettingsDescription"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843402"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="searchSuggestAuthority"
  type="int"
  transient="false"
@@ -15255,6 +15255,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ProgressBar_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973915"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_ProgressBar_Large"
  type="int"
  transient="false"
@@ -15266,6 +15277,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ProgressBar_Large_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973916"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_ProgressBar_Small"
  type="int"
  transient="false"
@@ -15277,6 +15299,17 @@
  visibility="public"
 >
 </field>
+<field name="Widget_ProgressBar_Small_Inverse"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973917"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="Widget_RatingBar"
  type="int"
  transient="false"
@@ -15508,39 +15541,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad20"
- type="int"
- transient="false"
- volatile="false"
- value="16973917"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad21"
- type="int"
- transient="false"
- volatile="false"
- value="16973916"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad22"
- type="int"
- transient="false"
- volatile="false"
- value="16973915"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad3"
  type="int"
  transient="false"
@@ -34778,6 +34778,17 @@
  visibility="public"
 >
 </field>
+<field name="ACTION_BATTERY_OKAY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.BATTERY_OKAY&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="ACTION_BOOT_COMPLETED"
  type="java.lang.String"
  transient="false"
@@ -60403,6 +60414,47 @@
 </package>
 <package name="android.graphics.drawable"
 >
+<interface name="Animatable"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="isRunning"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="start"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="stop"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
 <class name="AnimationDrawable"
  extends="android.graphics.drawable.DrawableContainer"
  abstract="false"
@@ -60411,6 +60463,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="android.graphics.drawable.Animatable">
+</implements>
 <implements name="java.lang.Runnable">
 </implements>
 <constructor name="AnimationDrawable"
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk
index 20f4adf..3daf44e 100644
--- a/cmds/keystore/Android.mk
+++ b/cmds/keystore/Android.mk
@@ -4,13 +4,14 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    keystore.c commands.c
+    netkeystore.c keymgmt.c
 
 LOCAL_C_INCLUDES := \
-    $(call include-path-for, system-core)/cutils
+    $(call include-path-for, system-core)/cutils \
+    external/openssl/include
 
 LOCAL_SHARED_LIBRARIES := \
-    libcutils
+    libcutils libssl
 
 LOCAL_STATIC_LIBRARIES :=
 
diff --git a/cmds/keystore/certtool.h b/cmds/keystore/certtool.h
new file mode 100644
index 0000000..7cd316b
--- /dev/null
+++ b/cmds/keystore/certtool.h
@@ -0,0 +1,83 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef __CERTTOOL_H__
+#define __CERTTOOL_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+
+#include "common.h"
+#include "netkeystore.h"
+
+/*
+ * The specific function 'get_cert' is used in daemons to get the key value
+ * from keystore. Caller should allocate the buffer and the length of the buffer
+ * should be MAX_KEY_VALUE_LENGTH.
+ */
+static inline int get_cert(char *certname, unsigned char *value, int *size)
+{
+    int count, fd, ret = -1;
+    LPC_MARSHAL cmd;
+    char delimiter[] = "_";
+    char *namespace, *keyname;
+    char *context = NULL;
+
+    if (value == NULL) {
+        LOGE("get_cert: value is null\n");
+        return -1;
+    }
+
+    fd = socket_local_client(SOCKET_PATH,
+                             ANDROID_SOCKET_NAMESPACE_RESERVED,
+                             SOCK_STREAM);
+    if (fd == -1) {
+        LOGE("Keystore service is not up and running.\n");
+        return -1;
+    }
+
+    cmd.opcode = GET;
+    if (((namespace = strtok_r(certname, delimiter, &context)) == NULL) ||
+        ((keyname = strtok_r(NULL, delimiter, &context)) == NULL)) {
+        goto err;
+    }
+    if ((cmd.len = snprintf((char*)cmd.data, BUFFER_MAX, "%s %s", namespace, keyname))
+        > (2 * MAX_KEY_NAME_LENGTH + 1)) goto err;
+
+    if (write_marshal(fd, &cmd)) {
+        LOGE("Incorrect command or command line is too long.\n");
+        goto err;
+    }
+    if (read_marshal(fd, &cmd)) {
+        LOGE("Failed to read the result.\n");
+        goto err;
+    }
+
+    // copy the result if succeeded.
+    if (!cmd.retcode && cmd.len <= BUFFER_MAX) {
+        memcpy(value, cmd.data, cmd.len);
+        ret = 0;
+        *size = cmd.len;
+    }
+err:
+    close(fd);
+    return ret;
+}
+
+#endif
diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c
deleted file mode 100644
index 17dd060..0000000
--- a/cmds/keystore/commands.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
-** Copyright 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.
-*/
-
-#include "keystore.h"
-
-static DIR *open_keystore(const char *dir)
-{
-    DIR *d;
-    if ((d = opendir(dir)) == NULL) {
-        if (mkdir(dir, 0770) < 0) {
-            LOGE("cannot create dir '%s': %s\n", dir, strerror(errno));
-            unlink(dir);
-            return NULL;
-        }
-        d = open_keystore(dir);
-    }
-    return d;
-}
-
-static int list_files(const char *dir, char reply[REPLY_MAX])
-{
-    struct dirent *de;
-    DIR *d;
-
-    if ((d = open_keystore(dir)) == NULL) {
-        return -1;
-    }
-    reply[0]=0;
-    while ((de = readdir(d))) {
-        if (de->d_type != DT_DIR) continue;
-        if ((strcmp(DOT, de->d_name) == 0) ||
-                (strcmp(DOTDOT, de->d_name) == 0)) continue;
-        if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX);
-        if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) {
-            LOGE("reply is too long(too many files under '%s'\n", dir);
-            return -1;
-        }
-    }
-    closedir(d);
-    return 0;
-}
-
-static int copy_keyfile(const char *src, int src_type, const char *dstfile) {
-    int srcfd = -1, dstfd;
-    char buf[REPLY_MAX];
-
-    if ((src_type == IS_FILE) && (srcfd = open(src, O_RDONLY)) == -1) {
-        LOGE("Cannot open the original file '%s'\n", src);
-        return -1;
-    }
-    if ((dstfd = open(dstfile, O_CREAT|O_RDWR)) == -1) {
-        LOGE("Cannot open the destination file '%s'\n", dstfile);
-        return -1;
-    }
-    if (src_type == IS_FILE) {
-        int length;
-        while((length = read(srcfd, buf, REPLY_MAX)) > 0) {
-            write(dstfd, buf, length);
-        }
-    } else {
-        write(dstfd, src, strlen(src));
-    }
-    close(srcfd);
-    close(dstfd);
-    chmod(dstfile, 0440);
-    return 0;
-}
-
-static int install_key(const char *path, const char *certname, const char *src,
-        int src_is_file, char *dstfile)
-{
-    struct dirent *de;
-    char fullpath[KEYNAME_LENGTH];
-    DIR *d;
-
-    if (snprintf(fullpath, sizeof(fullpath), "%s/%s/", path, certname)
-            >= KEYNAME_LENGTH) {
-        LOGE("cert name '%s' is too long.\n", certname);
-        return -1;
-    }
-
-    if ((d = open_keystore(fullpath)) == NULL) {
-        LOGE("Can not open the keystore '%s'\n", fullpath);
-        return -1;
-    }
-    closedir(d);
-    if (strlcat(fullpath, dstfile, KEYNAME_LENGTH) >= KEYNAME_LENGTH) {
-        LOGE("cert name '%s' is too long.\n", certname);
-        return -1;
-    }
-    return copy_keyfile(src, src_is_file, fullpath);
-}
-
-static int get_key(const char *path, const char *keyname, const char *file,
-        char reply[REPLY_MAX])
-{
-    struct dirent *de;
-    char filename[KEYNAME_LENGTH];
-    int fd;
-
-    if (snprintf(filename, sizeof(filename), "%s/%s/%s", path, keyname, file)
-            >= KEYNAME_LENGTH) {
-        LOGE("cert name '%s' is too long.\n", keyname);
-        return -1;
-    }
-
-    if ((fd = open(filename, O_RDONLY)) == -1) {
-        return -1;
-    }
-    close(fd);
-    strlcpy(reply, filename, REPLY_MAX);
-    return 0;
-}
-
-static int remove_key(const char *dir, const char *key)
-{
-    char dstfile[KEYNAME_LENGTH];
-    char *keyfile[4] = { USER_KEY, USER_P12_CERT, USER_CERTIFICATE,
-            CA_CERTIFICATE };
-    int i, count = 0;
-
-    for ( i = 0 ; i < 4 ; i++) {
-        if (snprintf(dstfile, KEYNAME_LENGTH, "%s/%s/%s", dir, key, keyfile[i])
-                >= KEYNAME_LENGTH) {
-            LOGE("keyname is too long '%s'\n", key);
-            return -1;
-        }
-        if (unlink(dstfile) == 0) count++;
-    }
-
-    if (count == 0) {
-        LOGE("can not clean up '%s' keys or not exist\n", key);
-        return -1;
-    }
-
-    snprintf(dstfile, KEYNAME_LENGTH, "%s/%s", dir, key);
-    if (rmdir(dstfile)) {
-        LOGE("can not clean up '%s' directory\n", key);
-        return -1;
-    }
-    return 0;
-}
-
-int list_user_certs(char reply[REPLY_MAX])
-{
-    return list_files(CERTS_DIR, reply);
-}
-
-int list_ca_certs(char reply[REPLY_MAX])
-{
-    return list_files(CACERTS_DIR, reply);
-}
-
-int install_user_cert(const char *keyname, const char *cert, const char *key)
-{
-    if (install_key(CERTS_DIR, keyname, cert, IS_FILE, USER_CERTIFICATE) == 0) {
-        return install_key(CERTS_DIR, keyname, key, IS_FILE, USER_KEY);
-    }
-    return -1;
-}
-
-int install_ca_cert(const char *keyname, const char *certfile)
-{
-    return install_key(CACERTS_DIR, keyname, certfile, IS_FILE, CA_CERTIFICATE);
-}
-
-int install_p12_cert(const char *keyname, const char *certfile)
-{
-    return install_key(CERTS_DIR, keyname, certfile, IS_FILE, USER_P12_CERT);
-}
-
-int add_ca_cert(const char *keyname, const char *certificate)
-{
-    return install_key(CACERTS_DIR, keyname, certificate, IS_CONTENT,
-            CA_CERTIFICATE);
-}
-
-int add_user_cert(const char *keyname, const char *certificate)
-{
-    return install_key(CERTS_DIR, keyname, certificate, IS_CONTENT,
-            USER_CERTIFICATE);
-}
-
-int add_user_key(const char *keyname, const char *key)
-{
-    return install_key(CERTS_DIR, keyname, key, IS_CONTENT, USER_KEY);
-}
-
-int get_ca_cert(const char *keyname, char reply[REPLY_MAX])
-{
-    return get_key(CACERTS_DIR, keyname, CA_CERTIFICATE, reply);
-}
-
-int get_user_cert(const char *keyname, char reply[REPLY_MAX])
-{
-    return get_key(CERTS_DIR, keyname, USER_CERTIFICATE, reply);
-}
-
-int get_user_key(const char *keyname, char reply[REPLY_MAX])
-{
-    if(get_key(CERTS_DIR, keyname, USER_KEY, reply))
-        return get_key(CERTS_DIR, keyname, USER_P12_CERT, reply);
-    return 0;
-}
-
-int remove_user_cert(const char *key)
-{
-    return remove_key(CERTS_DIR, key);
-}
-
-int remove_ca_cert(const char *key)
-{
-    return remove_key(CACERTS_DIR, key);
-}
diff --git a/cmds/keystore/common.h b/cmds/keystore/common.h
new file mode 100644
index 0000000..a18114e
--- /dev/null
+++ b/cmds/keystore/common.h
@@ -0,0 +1,60 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define SOCKET_PATH             "keystore"
+#define KEYSTORE_DIR            "/data/misc/keystore/"
+
+#define READ_TIMEOUT            3
+#define MAX_KEY_NAME_LENGTH     64
+#define MAX_NAMESPACE_LENGTH    MAX_KEY_NAME_LENGTH
+#define MAX_KEY_VALUE_LENGTH    4096
+
+#define BUFFER_MAX              MAX_KEY_VALUE_LENGTH
+
+typedef enum {
+    BOOTUP,
+    UNINITIALIZED,
+    LOCKED,
+    UNLOCKED,
+} KEYSTORE_STATE;
+
+typedef enum {
+    LOCK,
+    UNLOCK,
+    PASSWD,
+    GETSTATE,
+    LISTKEYS,
+    GET,
+    PUT,
+    REMOVE,
+    RESET,
+    MAX_OPCODE
+} KEYSTORE_OPCODE;
+
+typedef struct {
+    uint32_t  len;
+    union {
+        uint32_t  opcode;
+        uint32_t  retcode;
+    };
+    unsigned char data[BUFFER_MAX + 1];
+} LPC_MARSHAL;
+
+#endif
diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c
new file mode 100644
index 0000000..e4102a9
--- /dev/null
+++ b/cmds/keystore/keymgmt.c
@@ -0,0 +1,365 @@
+/*
+** Copyright 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <cutils/log.h>
+
+#include "common.h"
+#include "keymgmt.h"
+
+static int  retry_count = 0;
+static unsigned char iv[IV_LEN];
+static KEYSTORE_STATE state = BOOTUP;
+static AES_KEY encryptKey, decryptKey;
+
+inline void unlock_keystore(unsigned char *master_key)
+{
+    AES_set_encrypt_key(master_key, AES_KEY_LEN, &encryptKey);
+    AES_set_decrypt_key(master_key, AES_KEY_LEN, &decryptKey);
+    memset(master_key, 0, sizeof(master_key));
+    state = UNLOCKED;
+}
+
+inline void lock_keystore()
+{
+    memset(&encryptKey, 0 , sizeof(AES_KEY));
+    memset(&decryptKey, 0 , sizeof(AES_KEY));
+    state = LOCKED;
+}
+
+inline void get_encrypt_key(char *passwd, AES_KEY *key)
+{
+    unsigned char user_key[USER_KEY_LEN];
+    gen_key(passwd, user_key, USER_KEY_LEN);
+    AES_set_encrypt_key(user_key, AES_KEY_LEN, key);
+}
+
+inline void get_decrypt_key(char *passwd, AES_KEY *key)
+{
+    unsigned char user_key[USER_KEY_LEN];
+    gen_key(passwd, user_key, USER_KEY_LEN);
+    AES_set_decrypt_key(user_key, AES_KEY_LEN, key);
+}
+
+static int gen_random_blob(unsigned char *key, int size)
+{
+    int ret = 0;
+    int fd = open("/dev/urandom", O_RDONLY);
+    if (fd == -1) return -1;
+    if (read(fd, key, size) != size) ret = -1;
+    close(fd);
+    return ret;
+}
+
+static int encrypt_n_save(AES_KEY *enc_key, DATA_BLOB *blob,
+                          const char *keyfile)
+{
+    int size, fd, ret = -1;
+    unsigned char enc_blob[MAX_BLOB_LEN];
+
+    char tmpfile[KEYFILE_LEN];
+    strcpy(tmpfile, keyfile);
+    strcat(tmpfile, ".tmp");
+
+    // prepare the blob
+    memcpy(blob->iv, iv, IV_LEN);
+    blob->blob_size = get_blob_size(blob);
+    memcpy(enc_blob, blob->blob, blob->blob_size);
+    AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char *)blob->blob,
+                    blob->blob_size, enc_key, iv, AES_ENCRYPT);
+    // write to keyfile
+    size = data_blob_size(blob);
+    if ((fd = open(tmpfile, O_CREAT|O_RDWR)) == -1) return -1;
+    if (write(fd, blob, size) == size) ret = 0;
+    close(fd);
+    if (!ret) {
+        unlink(keyfile);
+        rename(tmpfile, keyfile);
+        chmod(keyfile, 0440);
+    }
+    return ret;
+}
+
+static int load_n_decrypt(const char *keyname, const char *keyfile,
+                          AES_KEY *key, DATA_BLOB *blob)
+{
+    int fd, ret = -1;
+    if ((fd = open(keyfile, O_RDONLY)) == -1) return -1;
+    // get the encrypted blob and iv
+    if ((read(fd, blob->iv, sizeof(blob->iv)) != sizeof(blob->iv)) ||
+        (read(fd, &blob->blob_size, sizeof(uint32_t)) != sizeof(uint32_t)) ||
+        (blob->blob_size > MAX_BLOB_LEN)) {
+        goto err;
+    } else {
+        unsigned char enc_blob[MAX_BLOB_LEN];
+        if (read(fd, enc_blob, blob->blob_size) !=
+            (int) blob->blob_size) goto err;
+        // decrypt the blob
+        AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char*)blob->blob,
+                        blob->blob_size, key, blob->iv, AES_DECRYPT);
+        if (strcmp(keyname, (char*)blob->keyname) == 0) ret = 0;
+    }
+err:
+    close(fd);
+    return ret;
+}
+
+static int store_master_key(char *upasswd, unsigned char *master_key)
+{
+    AES_KEY key;
+    DATA_BLOB blob;
+
+    // prepare the blob
+    strlcpy(blob.keyname, MASTER_KEY_TAG, USER_KEY_LEN);
+    blob.value_size = USER_KEY_LEN;
+    memcpy((void*)blob.value, (const void*)master_key, USER_KEY_LEN);
+
+    // generate the encryption key
+    get_encrypt_key(upasswd, &key);
+    return encrypt_n_save(&key, &blob, MASTER_KEY);
+}
+
+static int get_master_key(char *upasswd, unsigned char *master_key)
+{
+    AES_KEY key;
+    int size, ret = 0;
+    DATA_BLOB blob;
+
+    get_decrypt_key(upasswd, &key);
+    ret = load_n_decrypt(MASTER_KEY_TAG, MASTER_KEY, &key, &blob);
+    if (!ret) memcpy(master_key, blob.value, blob.value_size);
+    return ret;
+}
+
+static int create_master_key(char *upasswd)
+{
+    int ret;
+    unsigned char mpasswd[AES_KEY_LEN];
+    unsigned char master_key[USER_KEY_LEN];
+
+    gen_random_blob(mpasswd, AES_KEY_LEN);
+    gen_key((char*)mpasswd, master_key, USER_KEY_LEN);
+    if ((ret = store_master_key(upasswd, master_key)) == 0) {
+        unlock_keystore(master_key);
+    }
+    memset(master_key, 0, USER_KEY_LEN);
+    memset(mpasswd, 0, AES_KEY_LEN);
+
+    return ret;
+}
+
+static int change_passwd(char *data)
+{
+    unsigned char master_key[USER_KEY_LEN];
+    char *old_pass, *new_pass = NULL, *p, *delimiter=" ";
+    int ret, count = 0;
+    char *context = NULL;
+
+    old_pass = p = strtok_r(data, delimiter, &context);
+    while (p != NULL) {
+        count++;
+        new_pass = p;
+        p = strtok_r(NULL, delimiter, &context);
+    }
+    if (count != 2) return -1;
+    if ((ret = get_master_key(old_pass, master_key)) == 0) {
+        ret = store_master_key(new_pass, master_key);
+        retry_count = 0;
+    } else {
+        ret = MAX_RETRY_COUNT - ++retry_count;
+        if (ret == 0) {
+            retry_count = 0;
+            LOGE("passwd:reach max retry count, reset the keystore now.");
+            reset_keystore();
+            return -1;
+        }
+
+    }
+    return ret;
+}
+
+int remove_key(const char *namespace, const char *keyname)
+{
+    char keyfile[KEYFILE_LEN];
+
+    if (state != UNLOCKED) return -state;
+    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+    return unlink(keyfile);
+}
+
+int put_key(const char *namespace, const char *keyname,
+            unsigned char *data, int size)
+{
+    DATA_BLOB blob;
+    uint32_t  real_size;
+    char keyfile[KEYFILE_LEN];
+
+    if (state != UNLOCKED) {
+        LOGE("Can not store key with current state %d\n", state);
+        return -state;
+    }
+    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+    // flatten the args
+    strcpy(blob.keyname, keyname);
+    blob.value_size = size;
+    memcpy(blob.value, data, size);
+    return encrypt_n_save(&encryptKey, &blob, keyfile);
+}
+
+int get_key(const char *namespace, const char *keyname,
+            unsigned char *data, int *size)
+{
+    int ret;
+    DATA_BLOB blob;
+    uint32_t  blob_size;
+    char keyfile[KEYFILE_LEN];
+
+    if (state != UNLOCKED) {
+        LOGE("Can not retrieve key value with current state %d\n", state);
+        return -state;
+    }
+    sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+    ret = load_n_decrypt(keyname, keyfile, &decryptKey, &blob);
+    if (!ret) {
+        if ((blob.value_size > MAX_KEY_VALUE_LENGTH)) {
+            ret = -1;
+        } else {
+            *size = blob.value_size;
+            memcpy(data, blob.value, *size);
+        }
+    }
+    return ret;
+}
+
+int list_keys(const char *namespace, char reply[BUFFER_MAX])
+{
+    DIR *d;
+    struct dirent *de;
+
+    if (!namespace || ((d = opendir("."))) == NULL) {
+        LOGE("cannot open keystore dir or namespace is null\n");
+        return -1;
+    }
+    while ((de = readdir(d))) {
+        char *prefix, *name, *keyfile = de->d_name;
+        char *context = NULL;
+
+        if (de->d_type != DT_REG) continue;
+        if ((prefix = strtok_r(keyfile, NAME_DELIMITER, &context))
+            == NULL) continue;
+        if (strcmp(prefix, namespace)) continue;
+        if ((name = strtok_r(NULL, NAME_DELIMITER, &context)) == NULL) continue;
+        // append the key name into reply
+        if (reply[0] != 0) strlcat(reply, " ", BUFFER_MAX);
+        if (strlcat(reply, name, BUFFER_MAX) >= BUFFER_MAX) {
+            LOGE("too many files under keystore directory\n");
+            return -1;
+        }
+    }
+    closedir(d);
+    return 0;
+}
+
+int passwd(char *data)
+{
+    if (state == UNINITIALIZED) {
+        if (strchr(data, ' ')) return -1;
+        return create_master_key(data);
+    }
+    return change_passwd(data);
+}
+
+int lock()
+{
+    switch(state) {
+        case UNLOCKED:
+            lock_keystore();
+        case LOCKED:
+            return 0;
+        default:
+            return -1;
+    }
+}
+
+int unlock(char *passwd)
+{
+    unsigned char master_key[USER_KEY_LEN];
+    int ret = get_master_key(passwd, master_key);
+    if (!ret) {
+        unlock_keystore(master_key);
+        retry_count = 0;
+    } else {
+        ret = MAX_RETRY_COUNT - ++retry_count;
+        if (ret == 0) {
+            retry_count = 0;
+            LOGE("unlock:reach max retry count, reset the keystore now.");
+            reset_keystore();
+            return -1;
+        }
+    }
+    return ret;
+}
+
+KEYSTORE_STATE get_state()
+{
+    return state;
+}
+
+int reset_keystore()
+{
+    DIR *d;
+    struct dirent *de;
+
+    if ((d = opendir(".")) == NULL) {
+        LOGE("cannot open keystore dir\n");
+        return -1;
+    }
+    while ((de = readdir(d))) unlink(de->d_name);
+    closedir(d);
+    state = UNINITIALIZED;
+    LOGI("keystore is reset.");
+    return 0;
+}
+
+int init_keystore(const char *dir)
+{
+    int fd;
+
+    if (!dir) mkdir(dir, 0770);
+    if (!dir || chdir(dir)) {
+        LOGE("Can not open/create the keystore directory %s\n",
+             dir ? dir : "(null)");
+        return -1;
+    }
+    gen_random_blob(iv, IV_LEN);
+    if ((fd = open(MASTER_KEY, O_RDONLY)) == -1) {
+        state = UNINITIALIZED;
+        return 0;
+    }
+    close(fd);
+    state = LOCKED;
+    return 0;
+}
diff --git a/cmds/keystore/keymgmt.h b/cmds/keystore/keymgmt.h
new file mode 100644
index 0000000..0f10570
--- /dev/null
+++ b/cmds/keystore/keymgmt.h
@@ -0,0 +1,81 @@
+/*
+** Copyright 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.
+*/
+
+#ifndef __KEYMGMT_H__
+#define __KEYMGMT_H__
+
+#define MASTER_KEY_TAG  "master_key"
+#define MASTER_KEY      ".keymaster"
+#define MAX_PATH_LEN    128
+#define SALT            "Android Keystore 0.1"
+#define NAME_DELIMITER  "_"
+#define KEYFILE_NAME    "%s"NAME_DELIMITER"%s"
+#define KEYGEN_ITER     1024
+#define AES_KEY_LEN     128
+#define USER_KEY_LEN    (AES_KEY_LEN/8)
+#define IV_LEN          USER_KEY_LEN
+#define MAX_RETRY_COUNT 6
+
+#define gen_key(passwd, key, len) \
+                PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), \
+                                       (unsigned char*)SALT, \
+                                       strlen(SALT), KEYGEN_ITER, \
+                                       len, key)
+
+#define KEYFILE_LEN MAX_NAMESPACE_LENGTH + MAX_KEY_NAME_LENGTH + 6
+
+#define get_blob_size(blob) \
+        (((blob->value_size + sizeof(uint32_t) + MAX_KEY_NAME_LENGTH \
+        + USER_KEY_LEN - 1) / USER_KEY_LEN) * USER_KEY_LEN)
+
+#define MAX_BLOB_LEN    ((MAX_KEY_VALUE_LENGTH + MAX_KEY_NAME_LENGTH + \
+                         sizeof(uint32_t) + USER_KEY_LEN - 1) / USER_KEY_LEN)\
+                         * USER_KEY_LEN
+
+#define data_blob_size(blob) USER_KEY_LEN + sizeof(uint32_t) + blob->blob_size
+
+typedef struct {
+    unsigned char iv[USER_KEY_LEN];
+    uint32_t blob_size;
+    union {
+        unsigned char blob[1];
+        struct {
+            uint32_t value_size;
+            char keyname[MAX_KEY_NAME_LENGTH];
+            unsigned char value[MAX_KEY_VALUE_LENGTH];
+        } __attribute__((packed));
+    };
+} DATA_BLOB;
+
+typedef struct {
+    char tag[USER_KEY_LEN];
+    unsigned char master_key[USER_KEY_LEN];
+} MASTER_BLOB;
+
+int put_key(const char *namespace, const char *keyname,
+            unsigned char *data, int size);
+int get_key(const char *namespace, const char *keyname,
+            unsigned char *data, int *size);
+int remove_key(const char *namespace, const char *keyname);
+int list_keys(const char *namespace, char reply[BUFFER_MAX]);
+int passwd(char *data);
+int lock();
+int unlock(char *passwd);
+KEYSTORE_STATE get_state();
+int reset_keystore();
+int init_keystore(const char *dir);
+
+#endif
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
deleted file mode 100644
index df8d832..0000000
--- a/cmds/keystore/keystore.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
-** Copyright 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.
-*/
-
-#include "keystore.h"
-
-static inline int has_whitespace(char *name)
-{
-    if((strrchr(name, ' ') != NULL)) {
-        LOGE("'%s' contains whitespace character\n", name);
-        return 1;
-    }
-    return 0;
-}
-
-static int do_list_user_certs(char **arg, char reply[REPLY_MAX])
-{
-    return list_user_certs(reply);
-}
-
-static int do_list_ca_certs(char **arg, char reply[REPLY_MAX])
-{
-    return list_ca_certs(reply);
-}
-
-static int do_install_user_cert(char **arg, char reply[REPLY_MAX])
-{
-    if (has_whitespace(arg[0])) return -1;
-    /* copy the certificate and key to keystore */
-    return install_user_cert(arg[0], arg[1], arg[2]);
-}
-
-static int do_install_p12_cert(char **arg, char reply[REPLY_MAX])
-{
-    if (has_whitespace(arg[0])) return -1;
-    return install_p12_cert(arg[0], arg[1]);
-}
-
-static int do_install_ca_cert(char **arg, char reply[REPLY_MAX])
-{
-    if (has_whitespace(arg[0])) return -1;
-    /* copy the certificate and key to keystore */
-    return install_ca_cert(arg[0], arg[1]);
-}
-
-static int do_add_ca_cert(char **arg, char reply[REPLY_MAX])
-{
-    if (has_whitespace(arg[0])) return -1;
-    return add_ca_cert(arg[0], arg[1]);
-}
-
-static int do_add_user_cert(char **arg, char reply[REPLY_MAX])
-{
-    if (has_whitespace(arg[0])) return -1;
-    return add_user_cert(arg[0], arg[1]);
-}
-
-static int do_add_user_key(char **arg, char reply[REPLY_MAX])
-{
-    if (has_whitespace(arg[0])) return -1;
-    return add_user_key(arg[0], arg[1]);
-}
-
-static int do_get_ca_cert(char **arg, char reply[REPLY_MAX])
-{
-    return get_ca_cert(arg[0], reply);
-}
-
-static int do_get_user_cert(char **arg, char reply[REPLY_MAX])
-{
-    return get_user_cert(arg[0], reply);
-}
-
-static int do_get_user_key(char **arg, char reply[REPLY_MAX])
-{
-    return get_user_key(arg[0], reply);
-}
-
-static int do_remove_user_cert(char **arg, char reply[REPLY_MAX])
-{
-    return remove_user_cert(arg[0]);
-}
-
-static int do_remove_ca_cert(char **arg, char reply[REPLY_MAX])
-{
-    return remove_ca_cert(arg[0]);
-}
-
-
-struct cmdinfo {
-    const char *name;
-    unsigned numargs;
-    int (*func)(char **arg, char reply[REPLY_MAX]);
-};
-
-
-struct cmdinfo cmds[] = {
-    { "listcacerts",        0, do_list_ca_certs },
-    { "listusercerts",      0, do_list_user_certs },
-    { "installusercert",    3, do_install_user_cert },
-    { "installcacert",      2, do_install_ca_cert },
-    { "installp12cert",     2, do_install_p12_cert },
-    { "addusercert",        2, do_add_user_cert },
-    { "adduserkey",         2, do_add_user_key },
-    { "addcacert",          2, do_add_ca_cert },
-    { "getusercert",        1, do_get_user_cert },
-    { "getuserkey",         1, do_get_user_key },
-    { "getcacert",          1, do_get_ca_cert },
-    { "removecacert",       1, do_remove_ca_cert },
-    { "removeusercert",     1, do_remove_user_cert },
-};
-
-static int readx(int s, void *_buf, int count)
-{
-    char *buf = _buf;
-    int n = 0, r;
-    if (count < 0) return -1;
-    while (n < count) {
-        r = read(s, buf + n, count - n);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            LOGE("read error: %s\n", strerror(errno));
-            return -1;
-        }
-        if (r == 0) {
-            LOGE("eof\n");
-            return -1; /* EOF */
-        }
-        n += r;
-    }
-    return 0;
-}
-
-static int writex(int s, const void *_buf, int count)
-{
-    const char *buf = _buf;
-    int n = 0, r;
-    if (count < 0) return -1;
-    while (n < count) {
-        r = write(s, buf + n, count - n);
-        if (r < 0) {
-            if (errno == EINTR) continue;
-            LOGE("write error: %s\n", strerror(errno));
-            return -1;
-        }
-        n += r;
-    }
-    return 0;
-}
-
-
-/* Tokenize the command buffer, locate a matching command,
- * ensure that the required number of arguments are provided,
- * call the function(), return the result.
- */
-static int execute(int s, char cmd[BUFFER_MAX])
-{
-    char reply[REPLY_MAX];
-    char *arg[TOKEN_MAX+1];
-    unsigned i;
-    unsigned n = 0;
-    unsigned short count;
-    short ret = -1;
-
-    /* default reply is "" */
-    reply[0] = 0;
-
-    /* n is number of args (not counting arg[0]) */
-    arg[0] = cmd;
-    while (*cmd) {
-        if (*cmd == CMD_DELIMITER) {
-            *cmd++ = 0;
-            n++;
-            arg[n] = cmd;
-            if (n == TOKEN_MAX) {
-                LOGE("too many arguments\n");
-                goto done;
-            }
-        }
-        cmd++;
-    }
-
-    for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
-        if (!strcmp(cmds[i].name,arg[0])) {
-            if (n != cmds[i].numargs) {
-                LOGE("%s requires %d arguments (%d given)\n",
-                     cmds[i].name, cmds[i].numargs, n);
-            } else {
-                ret = (short) cmds[i].func(arg + 1, reply);
-            }
-            goto done;
-        }
-    }
-    LOGE("unsupported command '%s'\n", arg[0]);
-
-done:
-    if (reply[0]) {
-        strlcpy(cmd, reply, BUFFER_MAX);
-        count = strlen(cmd);
-    } else {
-        count = 0;
-    }
-    if (writex(s, &ret, sizeof(ret))) return -1;
-    if (ret == 0) {
-        if (writex(s, &count, sizeof(count))) return -1;
-        if (writex(s, cmd, count)) return -1;
-    }
-
-    return 0;
-}
-
-int shell_command(const int argc, const char **argv)
-{
-    int fd, i;
-    short ret;
-    unsigned short count;
-    char delimiter[2] = { CMD_DELIMITER, 0 };
-    char buf[BUFFER_MAX]="";
-
-    fd = socket_local_client(SOCKET_PATH,
-                             ANDROID_SOCKET_NAMESPACE_RESERVED,
-                             SOCK_STREAM);
-    if (fd == -1) {
-        fprintf(stderr, "Keystore service is not up and running\n");
-        exit(1);
-    }
-    for(i = 0; i < argc; i++) {
-        if (i > 0) strlcat(buf, delimiter, BUFFER_MAX);
-        if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
-            fprintf(stderr, "Arguments are too long\n");
-            exit(1);
-        }
-    }
-    count = strlen(buf);
-    if (writex(fd, &count, sizeof(count))) return -1;
-    if (writex(fd, buf, strlen(buf))) return -1;
-    if (readx(fd, &ret, sizeof(ret))) return -1;
-    if (ret == 0) {
-        if (readx(fd, &count, sizeof(count))) return -1;
-        if (readx(fd, buf, count)) return -1;
-        buf[count]=0;
-        fprintf(stdout, "%s\n", buf);
-    } else {
-        fprintf(stderr, "Failed, please check log!\n");
-    }
-    return 0;
-}
-
-int main(const int argc, const char *argv[])
-{
-    char buf[BUFFER_MAX];
-    struct sockaddr addr;
-    socklen_t alen;
-    int lsocket, s, count;
-
-    if (argc > 1) {
-        return shell_command(argc - 1, argv + 1);
-    }
-
-    lsocket = android_get_control_socket(SOCKET_PATH);
-    if (lsocket < 0) {
-        LOGE("Failed to get socket from environment: %s\n", strerror(errno));
-        exit(1);
-    }
-    if (listen(lsocket, 5)) {
-        LOGE("Listen on socket failed: %s\n", strerror(errno));
-        exit(1);
-    }
-    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
-
-    for (;;) {
-        alen = sizeof(addr);
-        s = accept(lsocket, &addr, &alen);
-        if (s < 0) {
-            LOGE("Accept failed: %s\n", strerror(errno));
-            continue;
-        }
-        fcntl(s, F_SETFD, FD_CLOEXEC);
-
-        LOGI("new connection\n");
-        for (;;) {
-            unsigned short count;
-            if (readx(s, &count, sizeof(count))) {
-                LOGE("failed to read size\n");
-                break;
-            }
-            if ((count < 1) || (count >= BUFFER_MAX)) {
-                LOGE("invalid size %d\n", count);
-                break;
-            }
-            if (readx(s, buf, count)) {
-                LOGE("failed to read command\n");
-                break;
-            }
-            buf[count] = 0;
-            if (execute(s, buf)) break;
-        }
-        LOGI("closing connection\n");
-        close(s);
-    }
-
-    return 0;
-}
diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h
deleted file mode 100644
index b9cb185..0000000
--- a/cmds/keystore/keystore.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-**
-** Copyright 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.
-*/
-
-#define LOG_TAG "keystore"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <utime.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <cutils/sockets.h>
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#define SOCKET_PATH "keystore"
-
-
-/* path of the keystore */
-
-#define KEYSTORE_DIR_PREFIX "/data/misc/keystore"
-#define CERTS_DIR           KEYSTORE_DIR_PREFIX "/keys"
-#define CACERTS_DIR         KEYSTORE_DIR_PREFIX "/cacerts"
-#define CA_CERTIFICATE      "ca.crt"
-#define USER_CERTIFICATE    "user.crt"
-#define USER_P12_CERT       "user.p12"
-#define USER_KEY            "user.key"
-#define DOT                 "."
-#define DOTDOT              ".."
-
-#define BUFFER_MAX      4096  /* input buffer for commands */
-#define TOKEN_MAX       8     /* max number of arguments in buffer */
-#define REPLY_MAX       4096  /* largest reply allowed */
-#define CMD_DELIMITER   '\t'
-#define KEYNAME_LENGTH  128
-#define IS_CONTENT      0
-#define IS_FILE         1
-
-
-/* commands.c */
-int list_ca_certs(char reply[REPLY_MAX]);
-int list_user_certs(char reply[REPLY_MAX]);
-int install_user_cert(const char *certname, const char *cert, const char *key);
-int install_ca_cert(const char *certname, const char *cert);
-int install_p12_cert(const char *certname, const char *cert);
-int add_ca_cert(const char *certname, const char *content);
-int add_user_cert(const char *certname, const char *content);
-int add_user_key(const char *keyname, const char *content);
-int get_ca_cert(const char *keyname, char reply[REPLY_MAX]);
-int get_user_cert(const char *keyname, char reply[REPLY_MAX]);
-int get_user_key(const char *keyname, char reply[REPLY_MAX]);
-int remove_user_cert(const char *certname);
-int remove_ca_cert(const char *certname);
diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c
new file mode 100644
index 0000000..e45e24f6
--- /dev/null
+++ b/cmds/keystore/netkeystore.c
@@ -0,0 +1,409 @@
+/*
+** Copyright 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.
+*/
+
+#define LOG_TAG "keystore"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <private/android_filesystem_config.h>
+
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "netkeystore.h"
+#include "keymgmt.h"
+
+#define  CMD_PUT_WITH_FILE  "putfile"
+
+typedef void CMD_FUNC(LPC_MARSHAL *cmd, LPC_MARSHAL *reply);
+
+struct cmdinfo {
+    const char *name;
+    CMD_FUNC *func;
+};
+
+static CMD_FUNC do_lock;
+static CMD_FUNC do_unlock;
+static CMD_FUNC do_passwd;
+static CMD_FUNC do_get_state;;
+static CMD_FUNC do_listkeys;
+static CMD_FUNC do_get_key;
+static CMD_FUNC do_put_key;
+static CMD_FUNC do_remove_key;
+static CMD_FUNC do_reset_keystore;
+
+#define str(x)      #x
+
+struct cmdinfo cmds[] = {
+    { str(LOCK),           do_lock },
+    { str(UNLOCK),         do_unlock },
+    { str(PASSWD),         do_passwd },
+    { str(GETSTATE),       do_get_state },
+    { str(LISTKEYS),       do_listkeys },
+    { str(GET),            do_get_key },
+    { str(PUT),            do_put_key },
+    { str(REMOVE),         do_remove_key },
+    { str(RESET),          do_reset_keystore },
+};
+
+static  struct ucred cr;
+
+static int check_get_perm(int uid)
+{
+    if (uid == AID_WIFI || uid == AID_VPN) return 0;
+    return -1;
+}
+
+static int check_reset_perm(int uid)
+{
+    if (uid == AID_SYSTEM) return 0;
+    return -1;
+}
+
+static int parse_keyname(char *name, uint32_t len,
+                         char *namespace, char *keyname)
+{
+    int count = 0;
+    char *c = namespace, *p = namespace, *t = name;
+
+    if (!name || !namespace || !keyname) return -1;
+    while (t < name + len && (*t != 0)) {
+        if (*t == ' ') {
+            if (c == keyname) return -1;
+            *p = count = 0;
+            c = p = keyname;
+            t++;
+        } else {
+            if (!isalnum(*t)) return -1;
+            *p++ = *t++;
+            // also check if the keyname/namespace is too long.
+            if (count++ == MAX_KEY_NAME_LENGTH) return -1;
+        }
+    }
+    *p = 0;
+    return 0;
+}
+
+// args of passwd():
+// firstPassword - for the first time
+// oldPassword newPassword - for changing the password
+static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = passwd((char*)cmd->data);
+}
+
+// args of lock():
+// no argument
+static void do_lock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = lock();
+}
+
+// args of unlock():
+// password
+static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = unlock((char*)cmd->data);
+}
+
+// args of get_state():
+// no argument
+static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = get_state();
+}
+
+// args of listkeys():
+// namespace
+static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    reply->retcode = list_keys((const char*)cmd->data, (char*)reply->data);
+    if (!reply->retcode) reply->len = strlen((char*)reply->data);
+}
+
+// args of get():
+// namespace keyname
+static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    char namespace[MAX_KEY_NAME_LENGTH];
+    char keyname[MAX_KEY_NAME_LENGTH];
+
+    if (check_get_perm(cr.uid)) {
+        LOGE("uid %d doesn't have the permission to get key value\n", cr.uid);
+        reply->retcode = -1;
+        return;
+    }
+
+    if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
+        reply->retcode = -1;
+    } else {
+        reply->retcode = get_key(namespace, keyname, reply->data,
+                                 (int*)&reply->len);
+    }
+}
+
+static int get_value_index(LPC_MARSHAL *cmd)
+{
+    uint32_t count = 0, i;
+    for (i = 0 ; i < cmd->len ; ++i) {
+        if (cmd->data[i] == ' ') {
+            if (++count == 2) return ++i;
+        }
+    }
+    return -1;
+}
+
+// args of put():
+// namespace keyname keyvalue
+static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    char namespace[MAX_KEY_NAME_LENGTH];
+    char keyname[MAX_KEY_NAME_LENGTH];
+
+    int p = get_value_index(cmd);
+    if (p == -1) {
+        reply->retcode = -1;
+    } else {
+        unsigned char *value;
+        if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) {
+            reply->retcode = -1;
+            return;
+        }
+        value = &cmd->data[p];
+        int len = cmd->len - p;
+        reply->retcode = put_key(namespace, keyname, value, len);
+    }
+}
+
+// args of remove_key():
+// namespace keyname
+static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    char namespace[MAX_KEY_NAME_LENGTH];
+    char keyname[MAX_KEY_NAME_LENGTH];
+    if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
+        reply->retcode = -1;
+        return;
+    }
+    reply->retcode = remove_key(namespace, keyname);
+}
+
+// args of reset_keystore():
+// no argument
+static void do_reset_keystore(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    if (check_reset_perm(cr.uid)) {
+        LOGE("uid %d doesn't have the permission to reset the keystore\n",
+             cr.uid);
+        reply->retcode = -1;
+        return;
+    }
+    reply->retcode = reset_keystore();
+}
+static void execute(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
+{
+    uint32_t cmd_max = sizeof(cmds)/sizeof(struct cmdinfo);
+
+    if (cmd->opcode >= cmd_max) {
+        LOGE("the opcode (%d) is not valid", cmd->opcode);
+        reply->retcode = -1;
+        return;
+    }
+    cmds[cmd->opcode].func(cmd, reply);
+}
+
+static int set_read_timeout(int socket)
+{
+    struct timeval tv;
+    tv.tv_sec = READ_TIMEOUT;
+    if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,  sizeof tv))
+    {
+        LOGE("setsockopt failed");
+        return -1;
+    }
+    return 0;
+}
+
+static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd)
+{
+    int fd, len, ret = 0;
+
+    // get opcode of the function put()
+    if ((fd = open(filename, O_RDONLY)) == -1) {
+        fprintf(stderr, "Can not open file %s\n", filename);
+        return -1;
+    }
+    cmd->data[cmd->len] = ' ';
+    cmd->len++;
+    len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len);
+    if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) {
+        ret = -1;
+    } else {
+        cmd->len += len;
+    }
+    close(fd);
+    return ret;
+}
+
+static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd)
+{
+    int i, len = 0;
+    char *buf = (char*)cmd->data;
+    buf[0] = 0;
+    for (i = 0 ; i < argc ; ++i) {
+        if (i == 0) {
+            len = strlcpy(buf, argv[i], BUFFER_MAX);
+        } else {
+            len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]);
+        }
+        if (len >= BUFFER_MAX) return -1;
+    }
+    if (len) cmd->len = len;
+    return 0;
+}
+
+static int parse_cmd(int argc, const char **argv, LPC_MARSHAL *cmd)
+{
+    uint32_t i, len = 0;
+    uint32_t cmd_max = sizeof(cmds)/sizeof(cmds[0]);
+
+    for (i = 0 ; i < cmd_max ; ++i) {
+        if (!strcasecmp(argv[0], cmds[i].name)) break;
+    }
+
+    if (i == cmd_max) {
+        // check if this is a command to put the key value with a file.
+        if (strcmp(argv[0], CMD_PUT_WITH_FILE) != 0) return -1;
+        cmd->opcode = PUT;
+        if (argc != 4) {
+            fprintf(stderr, "%s args\n\tnamespace keyname filename\n",
+                    argv[0]);
+            return -1;
+        }
+        if (flatten_str_args(argc - 2, argv + 1, cmd)) return -1;
+        return append_input_from_file(argv[3], cmd);
+    } else {
+        cmd->opcode = i;
+        return flatten_str_args(argc - 1, argv + 1, cmd);
+    }
+}
+
+static int shell_command(const int argc, const char **argv)
+{
+    int fd, i;
+    LPC_MARSHAL  cmd;
+
+    if (parse_cmd(argc, argv , &cmd)) {
+        fprintf(stderr, "Incorrect command or command line is too long.\n");
+        exit(1);
+    }
+    fd = socket_local_client(SOCKET_PATH,
+                             ANDROID_SOCKET_NAMESPACE_RESERVED,
+                             SOCK_STREAM);
+    if (fd == -1) {
+        fprintf(stderr, "Keystore service is not up and running.\n");
+        exit(1);
+    }
+
+    if (write_marshal(fd, &cmd)) {
+        fprintf(stderr, "Incorrect command or command line is too long.\n");
+        exit(1);
+    }
+    if (read_marshal(fd, &cmd)) {
+        fprintf(stderr, "Failed to read the result.\n");
+        exit(1);
+    }
+    cmd.data[cmd.len] = 0;
+    fprintf(stdout, "%s\n", (cmd.retcode == 0) ? "Succeeded!" : "Failed!");
+    if (cmd.len) fprintf(stdout, "\t%s\n", (char*)cmd.data);
+    close(fd);
+    return 0;
+}
+
+int main(const int argc, const char *argv[])
+{
+    struct sockaddr addr;
+    socklen_t alen;
+    int lsocket, s;
+    LPC_MARSHAL  cmd, reply;
+
+    if (argc > 1) {
+        return shell_command(argc - 1, argv + 1);
+    }
+
+    if (init_keystore(KEYSTORE_DIR)) {
+        LOGE("Can not initialize the keystore, the directory exist?\n");
+        exit(1);
+    }
+
+    lsocket = android_get_control_socket(SOCKET_PATH);
+    if (lsocket < 0) {
+        LOGE("Failed to get socket from environment: %s\n", strerror(errno));
+        exit(1);
+    }
+    if (listen(lsocket, 5)) {
+        LOGE("Listen on socket failed: %s\n", strerror(errno));
+        exit(1);
+    }
+    fcntl(lsocket, F_SETFD, FD_CLOEXEC);
+    memset(&reply, 0, sizeof(LPC_MARSHAL));
+
+    for (;;) {
+        socklen_t cr_size = sizeof(cr);
+        alen = sizeof(addr);
+        s = accept(lsocket, &addr, &alen);
+
+        /* retrieve the caller info here */
+        if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
+            close(s);
+            LOGE("Unable to recieve socket options\n");
+            continue;
+        }
+
+        if (s < 0) {
+            LOGE("Accept failed: %s\n", strerror(errno));
+            continue;
+        }
+        fcntl(s, F_SETFD, FD_CLOEXEC);
+        if (set_read_timeout(s)) {
+            close(s);
+            continue;
+        }
+
+        // read the command, execute and send the result back.
+        if(read_marshal(s, &cmd)) goto err;
+        LOGI("new connection\n");
+        execute(&cmd, &reply);
+        write_marshal(s, &reply);
+err:
+        memset(&reply, 0, sizeof(LPC_MARSHAL));
+        LOGI("closing connection\n");
+        close(s);
+    }
+
+    return 0;
+}
diff --git a/cmds/keystore/netkeystore.h b/cmds/keystore/netkeystore.h
new file mode 100644
index 0000000..a87a667
--- /dev/null
+++ b/cmds/keystore/netkeystore.h
@@ -0,0 +1,96 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef __NETKEYSTORE_H__
+#define __NETKEYSTORE_H__
+
+#include <stdio.h>
+#include <cutils/sockets.h>
+#include <cutils/log.h>
+
+#include "common.h"
+
+static inline int readx(int s, void *_buf, int count)
+{
+    char *buf = _buf;
+    int n = 0, r;
+    if (count < 0) return -1;
+    while (n < count) {
+        r = read(s, buf + n, count - n);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            LOGE("read error: %s\n", strerror(errno));
+            return -1;
+        }
+        if (r == 0) {
+            LOGE("eof\n");
+            return -1; /* EOF */
+        }
+        n += r;
+    }
+    return 0;
+}
+
+static inline int writex(int s, const void *_buf, int count)
+{
+    const char *buf = _buf;
+    int n = 0, r;
+    if (count < 0) return -1;
+    while (n < count) {
+        r = write(s, buf + n, count - n);
+        if (r < 0) {
+            if (errno == EINTR) continue;
+            LOGE("write error: %s\n", strerror(errno));
+            return -1;
+        }
+        n += r;
+    }
+    return 0;
+}
+
+static inline int read_marshal(int s, LPC_MARSHAL *cmd)
+{
+    if (readx(s, cmd, 2 * sizeof(uint32_t))) {
+        LOGE("failed to read header\n");
+        return -1;
+    }
+    if (cmd->len > BUFFER_MAX) {
+        LOGE("invalid size %d\n", cmd->len);
+        return -1;
+    }
+    if (readx(s, cmd->data, cmd->len)) {
+        LOGE("failed to read data\n");
+        return -1;
+    }
+    cmd->data[cmd->len] = 0;
+    return 0;
+}
+
+static inline int write_marshal(int s, LPC_MARSHAL *cmd)
+{
+    if (writex(s, cmd, 2 * sizeof(uint32_t))) {
+        LOGE("failed to write marshal header\n");
+        return -1;
+    }
+    if (writex(s, cmd->data, cmd->len)) {
+        LOGE("failed to write marshal data\n");
+        return -1;
+    }
+    return 0;
+}
+
+#endif
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 62dc651..5ee29ac 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3981,7 +3981,10 @@
             ProviderRecord pr = mProviderMap.get(name);
             if (pr.mProvider.asBinder() == provider.asBinder()) {
                 Log.i(TAG, "Removing dead content provider: " + name);
-                mProviderMap.remove(name);
+                ProviderRecord removed = mProviderMap.remove(name);
+                if (removed != null) {
+                    removed.mProvider.asBinder().unlinkToDeath(removed, 0);
+                }
             }
         }
     }
@@ -3990,7 +3993,10 @@
         ProviderRecord pr = mProviderMap.get(name);
         if (pr.mProvider.asBinder() == provider.asBinder()) {
             Log.i(TAG, "Removing dead content provider: " + name);
-            mProviderMap.remove(name);
+            ProviderRecord removed = mProviderMap.remove(name);
+            if (removed != null) {
+                removed.mProvider.asBinder().unlinkToDeath(removed, 0);
+            }
         }
     }
 
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 6fe4896..0785029 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Animatable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -241,8 +242,15 @@
         // Reset any stored values from last time dialog was shown.
         mStoredComponentName = null;
         mStoredAppSearchData = null;
-        
-        return doShow(initialQuery, selectInitialQuery, componentName, appSearchData, globalSearch);
+
+        boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData,
+                globalSearch);
+        if (success) {
+            // Display the drop down as soon as possible instead of waiting for the rest of the
+            // pending UI stuff to get done, so that things appear faster to the user.
+            mSearchAutoComplete.showDropDownAfterLayout();
+        }
+        return success;
     }
     
     /**
@@ -422,11 +430,11 @@
         if (working) {
             mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
                     null, null, mWorkingSpinner, null);
-//            mWorkingSpinner.start();
+            ((Animatable) mWorkingSpinner).start();
         } else {
             mSearchAutoComplete.setCompoundDrawablesWithIntrinsicBounds(
                     null, null, null, null);
-//            mWorkingSpinner.stop();
+            ((Animatable) mWorkingSpinner).stop();
         }
     }
     
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 10c2b02..03e8623 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -26,7 +26,6 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
@@ -40,7 +39,7 @@
     static final int HANDLE_UPDATE = 1;
     static final int HANDLE_PROVIDER_CHANGED = 2;
 
-    static Object sServiceLock = new Object();
+    final static Object sServiceLock = new Object();
     static IAppWidgetService sService;
 
     Context mContext;
@@ -85,7 +84,7 @@
 
     int mHostId;
     Callbacks mCallbacks = new Callbacks();
-    HashMap<Integer,AppWidgetHostView> mViews = new HashMap();
+    final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>();
 
     public AppWidgetHost(Context context, int hostId) {
         mContext = context;
@@ -104,8 +103,8 @@
      * becomes visible, i.e. from onStart() in your Activity.
      */
     public void startListening() {
-        int[] updatedIds = null;
-        ArrayList<RemoteViews> updatedViews = new ArrayList();
+        int[] updatedIds;
+        ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
         
         try {
             if (mPackageName == null) {
@@ -209,7 +208,7 @@
         synchronized (mViews) {
             mViews.put(appWidgetId, view);
         }
-        RemoteViews views = null;
+        RemoteViews views;
         try {
             views = sService.getAppWidgetViews(appWidgetId);
         } catch (RemoteException e) {
@@ -231,6 +230,7 @@
     /**
      * Called when the AppWidget provider for a AppWidget has been upgraded to a new apk.
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
     }
 
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index be0f96e..307c4c8 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -22,16 +22,12 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.os.Handler;
-import android.os.Message;
 import android.os.SystemClock;
-import android.util.Config;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.Animation;
 import android.widget.FrameLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -86,6 +82,7 @@
      * @param animationIn Resource ID of in animation to use
      * @param animationOut Resource ID of out animation to use
      */
+    @SuppressWarnings({"UnusedDeclaration"})
     public AppWidgetHostView(Context context, int animationIn, int animationOut) {
         super(context);
         mContext = context;
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index eca04b3..3660001 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -21,7 +21,9 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 import android.widget.RemoteViews;
 
 import com.android.internal.appwidget.IAppWidgetService;
@@ -187,6 +189,8 @@
     
     Context mContext;
 
+    private DisplayMetrics mDisplayMetrics;
+
     /**
      * Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
      * Context} object.
@@ -213,6 +217,7 @@
 
     private AppWidgetManager(Context context) {
         mContext = context;
+        mDisplayMetrics = context.getResources().getDisplayMetrics();
     }
 
     /**
@@ -292,7 +297,15 @@
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         try {
-            return sService.getAppWidgetInfo(appWidgetId);
+            AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
+            if (info != null) {
+                // Converting complex to dp.
+                info.minWidth = 
+                        TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
+                info.minHeight =
+                        TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
+            }
+            return info;
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 87f762d..ca4bea8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1085,6 +1085,27 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
     
+    /**
+     * Activity Action: Setup wizard to launch after a platform update.  This
+     * activity should have a string meta-data field associated with it,
+     * {@link #METADATA_SETUP_VERSION}, which defines the current version of
+     * the platform for setup.  The activity will be launched only if
+     * {@link android.provider.Settings.Secure#LAST_SETUP_SHOWN} is not the
+     * same value.
+     * <p>Input: Nothing.
+     * <p>Output: Nothing.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
+    
+    /**
+     * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
+     * describing the last run version of the platform that was setup.
+     * @hide
+     */
+    public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
@@ -1290,6 +1311,13 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
     /**
+     * Broadcast Action:  Indicates the battery is now okay after being low.
+     * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has
+     * gone back up to an okay state.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY";
+    /**
      * Broadcast Action:  External power has been connected to the device.
      * This is intended for applications that wish to register specifically to this notification.
      * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
index fb121bb..aebcb3c 100755
--- a/core/java/android/os/IHardwareService.aidl
+++ b/core/java/android/os/IHardwareService.aidl
@@ -20,9 +20,9 @@
 interface IHardwareService
 {
     // Vibrator support
-    void vibrate(long milliseconds);
+    void vibrate(long milliseconds, IBinder token);
     void vibratePattern(in long[] pattern, int repeat, IBinder token);
-    void cancelVibrate();
+    void cancelVibrate(IBinder token);
     
     // flashlight support
     boolean getFlashlightEnabled();
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 0f75289..51dcff1 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -24,6 +24,7 @@
 public class Vibrator
 {
     IHardwareService mService;
+    private final Binder mToken = new Binder();
 
     /** @hide */
     public Vibrator()
@@ -40,7 +41,7 @@
     public void vibrate(long milliseconds)
     {
         try {
-            mService.vibrate(milliseconds);
+            mService.vibrate(milliseconds, mToken);
         } catch (RemoteException e) {
         }
     }
@@ -65,7 +66,7 @@
         // anyway
         if (repeat < pattern.length) {
             try {
-                mService.vibratePattern(pattern, repeat, new Binder());
+                mService.vibratePattern(pattern, repeat, mToken);
             } catch (RemoteException e) {
             }
         } else {
@@ -79,7 +80,7 @@
     public void cancel()
     {
         try {
-            mService.cancelVibrate();
+            mService.cancelVibrate(mToken);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 303d3fc..79f26978 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2248,6 +2248,26 @@
         public static final String USE_LOCATION_FOR_SERVICES = "use_location";
 
         /**
+         * Controls whether data backup is enabled.
+         * Type: int ( 0 = disabled, 1 = enabled )
+         * @hide
+         */
+        public static final String BACKUP_ENABLED = "backup_enabled";
+
+        /**
+         * Component of the transport to use for backup/restore.
+         * @hide
+         */
+        public static final String BACKUP_TRANSPORT = "backup_transport";
+        
+        /**
+         * Version for which the setup wizard was last shown.  Bumped for
+         * each release when there is new setup information to show.
+         * @hide
+         */
+        public static final String LAST_SETUP_SHOWN = "last_setup_shown";
+
+        /**
          * Helper method for determining if a location provider is enabled.
          * @param cr the content resolver to use
          * @param provider the location provider to query
@@ -2871,6 +2891,13 @@
                 "vending_heartbeat_frequency_ms";
 
         /**
+         * Frequency in milliseconds at which we should resend pending download
+         * requests to the API Server from the Vending Machine client.
+         */
+        public static final String VENDING_PENDING_DOWNLOAD_RESEND_FREQUENCY_MS =
+                "vending_pd_resend_frequency_ms";
+
+        /**
          * URL that points to the legal terms of service to display in Settings.
          * <p>
          * This should be a https URL. For a pretty user-friendly URL, use
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index 90dfa0b..8ef1f15 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -67,6 +67,7 @@
     private final int mSearchImeOptions;
     private final boolean mIncludeInGlobalSearch;
     private final boolean mQueryAfterZeroResults;
+    private final String mSettingsDescription;
     private final String mSuggestAuthority;
     private final String mSuggestPath;
     private final String mSuggestSelection;
@@ -134,6 +135,14 @@
     public boolean shouldRewriteQueryFromText() {
         return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT);
     }
+    
+    /**
+     * Gets the description to use for this source in system search settings, or null if
+     * none has been specified.
+     */
+    public String getSettingsDescription() {
+        return mSettingsDescription;
+    }
 
     /**
      * Retrieve the path for obtaining search suggestions.
@@ -280,6 +289,8 @@
         mQueryAfterZeroResults = a.getBoolean(
                 com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false);
 
+        mSettingsDescription = a.getString(
+                com.android.internal.R.styleable.Searchable_searchSettingsDescription);
         mSuggestAuthority = a.getString(
                 com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
         mSuggestPath = a.getString(
@@ -448,6 +459,7 @@
                         + ",suggestAuthority=" + searchable.getSuggestAuthority()
                         + ",target=" + searchable.getSearchActivity().getClassName()
                         + ",global=" + searchable.shouldIncludeInGlobalSearch()
+                        + ",settingsDescription=" + searchable.getSettingsDescription()
                         + ",threshold=" + searchable.getSuggestThreshold());
             } else {
                 Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data");
@@ -686,7 +698,8 @@
         mSearchImeOptions = in.readInt();
         mIncludeInGlobalSearch = in.readInt() != 0;
         mQueryAfterZeroResults = in.readInt() != 0;
-
+        
+        mSettingsDescription = in.readString();
         mSuggestAuthority = in.readString();
         mSuggestPath = in.readString();
         mSuggestSelection = in.readString();
@@ -723,6 +736,7 @@
         dest.writeInt(mIncludeInGlobalSearch ? 1 : 0);
         dest.writeInt(mQueryAfterZeroResults ? 1 : 0);
         
+        dest.writeString(mSettingsDescription);
         dest.writeString(mSuggestAuthority);
         dest.writeString(mSuggestPath);
         dest.writeString(mSuggestSelection);
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index f2fb9cb..dfc16f5 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -81,6 +81,12 @@
 
         switch (event.getAction()) {
         case MotionEvent.ACTION_DOWN:
+            ds = buffer.getSpans(0, buffer.length(), DragState.class);
+
+            for (int i = 0; i < ds.length; i++) {
+                buffer.removeSpan(ds[i]);
+            }
+
             buffer.setSpan(new DragState(event.getX(), event.getY(),
                             widget.getScrollX(), widget.getScrollY()),
                     0, 0, Spannable.SPAN_MARK_MARK);
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 25a20f2..23c7f7d 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -93,6 +93,28 @@
         // Treat ACTION_DOWN and ACTION MULTIPLE the same
         boolean down = event.getAction() != KeyEvent.ACTION_UP;
         int keyCode = event.getKeyCode();
+
+        boolean isArrowKey = false;
+        switch(keyCode) {
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+                if (!mWebView.nativeCursorMatchesFocus()) {
+                    return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+                            .onKeyUp(keyCode, event);
+
+                }
+                isArrowKey = true;
+                break;
+        }
+
+        if (!isArrowKey && mWebView.nativeFocusNodePointer() != mNodePointer) {
+            mWebView.nativeClearCursor();
+            remove();
+            return mWebView.dispatchKeyEvent(event);
+        }
+
         Spannable text = (Spannable) getText();
         int oldLength = text.length();
         // Normally the delete key's dom events are sent via onTextChanged.
@@ -133,20 +155,6 @@
             // Pass to super to handle longpress.
             return super.dispatchKeyEvent(event);
         }
-        boolean isArrowKey = false;
-        switch(keyCode) {
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-                if (!mWebView.nativeCursorMatchesFocus()) {
-                    return down ? mWebView.onKeyDown(keyCode, event) : mWebView
-                            .onKeyUp(keyCode, event);
-
-                }
-                isArrowKey = true;
-                break;
-        }
 
         // Ensure there is a layout so arrow keys are handled properly.
         if (getLayout() == null) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index e061a4c..57b9ac3 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4504,6 +4504,15 @@
         mCallbackProxy.uiOverrideUrlLoading(url);
     }
 
+    // called by JNI
+    private void sendPluginState(int state) {
+        WebViewCore.PluginStateData psd = new WebViewCore.PluginStateData();
+        psd.mFrame = nativeCursorFramePointer();
+        psd.mNode = nativeCursorNodePointer();
+        psd.mState = state;
+        mWebViewCore.sendMessage(EventHub.PLUGIN_STATE, psd);
+    }
+
     @Override
     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
         boolean result = false;
@@ -4895,8 +4904,8 @@
                     // sure the text edit box is still on the  screen.
                     if (inEditingMode() && nativeCursorIsTextInput()) {
                         mWebTextView.bringIntoView();
+                        rebuildWebTextView();
                     }
-                    rebuildWebTextView();
                     break;
                 case CLEAR_TEXT_ENTRY:
                     clearTextEntry();
@@ -5386,7 +5395,7 @@
         nativeUpdateCachedTextfield(updatedText, mTextGeneration);
     }
 
-    private native void     nativeClearCursor();
+    /* package */ native void nativeClearCursor();
     private native void     nativeCreate(int ptr);
     private native int      nativeCursorFramePointer();
     private native Rect     nativeCursorNodeBounds();
@@ -5423,6 +5432,7 @@
     /* package */ native int nativeFocusCandidatePointer();
     private native String   nativeFocusCandidateText();
     private native int      nativeFocusCandidateTextSize();
+    /* package */ native int nativeFocusNodePointer();
     private native Rect     nativeGetCursorRingBounds();
     private native Region   nativeGetSelection();
     private native boolean  nativeHasCursorNode();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index d8d9808..44d8b7e 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -440,6 +440,8 @@
      */
     private native void nativeSetDatabaseQuota(long quota);
 
+    private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
+
     // EventHub for processing messages
     private final EventHub mEventHub;
     // WebCore thread handler
@@ -570,6 +572,12 @@
         int mY;
     }
 
+    static class PluginStateData {
+        int mFrame;
+        int mNode;
+        int mState;
+    }
+
         static final String[] HandlerDebugString = {
             "LOAD_URL", // = 100;
             "STOP_LOADING", // = 101;
@@ -598,7 +606,7 @@
             "SINGLE_LISTBOX_CHOICE", // = 124;
             "MESSAGE_RELAY", // = 125;
             "SET_BACKGROUND_COLOR", // = 126;
-            "127", // = 127;
+            "PLUGIN_STATE", // = 127;
             "SAVE_DOCUMENT_STATE", // = 128;
             "GET_SELECTION", // = 129;
             "WEBKIT_DRAW", // = 130;
@@ -648,6 +656,7 @@
         static final int SINGLE_LISTBOX_CHOICE = 124;
         static final int MESSAGE_RELAY = 125;
         static final int SET_BACKGROUND_COLOR = 126;
+        static final int PLUGIN_STATE = 127; // plugin notifications
         static final int SAVE_DOCUMENT_STATE = 128;
         static final int GET_SELECTION = 129;
         static final int WEBKIT_DRAW = 130;
@@ -885,6 +894,11 @@
                             nativeFreeMemory();
                             break;
 
+                        case PLUGIN_STATE:
+                            PluginStateData psd = (PluginStateData) msg.obj;
+                            nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
+                            break;
+
                         case SET_NETWORK_STATE:
                             if (BrowserFrame.sJavaBridge == null) {
                                 throw new IllegalStateException("No WebView " +
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index ba47df5..374b0ae 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -124,6 +124,7 @@
     private boolean mBlockCompletion;
 
     private AutoCompleteTextView.ListSelectorHider mHideSelector;
+    private Runnable mShowDropDownRunnable;
 
     private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
 
@@ -1066,6 +1067,15 @@
     }
 
     /**
+     * Issues a runnable to show the dropdown as soon as possible.
+     *
+     * @hide internal used only by Search Dialog
+     */
+    public void showDropDownAfterLayout() {
+        post(mShowDropDownRunnable);
+    }
+
+    /**
      * <p>Displays the drop down on screen.</p>
      */
     public void showDropDown() {
@@ -1173,6 +1183,22 @@
 
             mHideSelector = new ListSelectorHider();
 
+            /**
+             * This Runnable exists for the sole purpose of checking if the view layout has got
+             * completed and if so call showDropDown to display the drop down. This is used to show
+             * the drop down as soon as possible after user opens up the search dialog, without
+             * waiting for the normal UI pipeline to do it's job which is slower than this method.
+             */
+            mShowDropDownRunnable = new Runnable() {
+                public void run() {
+                    // View layout should be all done before displaying the drop down.
+                    View view = getDropDownAnchorView();
+                    if (view != null && view.getWindowToken() != null) {
+                        showDropDown();
+                    }
+                }
+            };
+
             mDropDownList = new DropDownListView(context);
             mDropDownList.setSelector(mDropDownListHighlight);
             mDropDownList.setAdapter(mAdapter);
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 441414a..2c9e71e 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -30,6 +30,7 @@
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.Animatable;
 import android.graphics.drawable.shapes.RoundRectShape;
 import android.graphics.drawable.shapes.Shape;
 import android.util.AttributeSet;
@@ -683,7 +684,7 @@
             return;
         }
 
-        if (mIndeterminateDrawable instanceof AnimationDrawable) {
+        if (mIndeterminateDrawable instanceof Animatable) {
             mShouldStartAnimationDrawable = true;
             mAnimation = null;
         } else {
@@ -708,8 +709,8 @@
     void stopAnimation() {
         mAnimation = null;
         mTransformation = null;
-        if (mIndeterminateDrawable instanceof AnimationDrawable) {
-            ((AnimationDrawable) mIndeterminateDrawable).stop();
+        if (mIndeterminateDrawable instanceof Animatable) {
+            ((Animatable) mIndeterminateDrawable).stop();
             mShouldStartAnimationDrawable = false;
         }
     }
@@ -818,8 +819,8 @@
             }
             d.draw(canvas);
             canvas.restore();
-            if (mShouldStartAnimationDrawable && d instanceof AnimationDrawable) {
-                ((AnimationDrawable) d).start();
+            if (mShouldStartAnimationDrawable && d instanceof Animatable) {
+                ((Animatable) d).start();
                 mShouldStartAnimationDrawable = false;
             }
         }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7936f65..1b5c5bc 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,10 +20,8 @@
 import android.app.PendingIntent.CanceledException;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Parcel;
@@ -36,15 +34,12 @@
 import android.view.ViewGroup;
 import android.view.LayoutInflater.Filter;
 import android.view.View.OnClickListener;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
 
 import java.lang.Class;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 
@@ -69,13 +64,7 @@
      * The resource ID of the layout file. (Added to the parcel)
      */
     private int mLayoutId;
-    
-    /**
-     * The Context object used to inflate the layout file. Also may
-     * be used by actions if they need access to the senders resources.
-     */
-    private Context mContext;
-    
+
     /**
      * An array of actions to perform on the view tree once it has been
      * inflated
@@ -85,7 +74,7 @@
     
     /**
      * This annotation indicates that a subclass of View is alllowed to be used
-     * with the {@link android.widget.RemoteViews} mechanism.
+     * with the {@link RemoteViews} mechanism.
      */
     @Target({ ElementType.TYPE })
     @Retention(RetentionPolicy.RUNTIME)
@@ -116,7 +105,7 @@
         public int describeContents() {
             return 0;
         }
-    };
+    }
 
     /**
      * Equivalent to calling
@@ -232,15 +221,17 @@
                 targetDrawable = imageView.getDrawable();
             }
             
-            // Perform modifications only if values are set correctly
-            if (alpha != -1) {
-                targetDrawable.setAlpha(alpha);
-            }
-            if (colorFilter != -1 && filterMode != null) {
-                targetDrawable.setColorFilter(colorFilter, filterMode);
-            }
-            if (level != -1) {
-                targetDrawable.setLevel(level);
+            if (targetDrawable != null) {
+                // Perform modifications only if values are set correctly
+                if (alpha != -1) {
+                    targetDrawable.setAlpha(alpha);
+                }
+                if (colorFilter != -1 && filterMode != null) {
+                    targetDrawable.setColorFilter(colorFilter, filterMode);
+                }
+                if (level != -1) {
+                    targetDrawable.setLevel(level);
+                }
             }
         }
         
@@ -289,6 +280,7 @@
             this.viewId = in.readInt();
             this.methodName = in.readString();
             this.type = in.readInt();
+            //noinspection ConstantIfStatement
             if (false) {
                 Log.d("RemoteViews", "read viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
@@ -340,31 +332,32 @@
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
             out.writeInt(this.type);
+            //noinspection ConstantIfStatement
             if (false) {
                 Log.d("RemoteViews", "write viewId=0x" + Integer.toHexString(this.viewId)
                         + " methodName=" + this.methodName + " type=" + this.type);
             }
             switch (this.type) {
                 case BOOLEAN:
-                    out.writeInt(((Boolean)this.value).booleanValue() ? 1 : 0);
+                    out.writeInt((Boolean) this.value ? 1 : 0);
                     break;
                 case BYTE:
-                    out.writeByte(((Byte)this.value).byteValue());
+                    out.writeByte((Byte) this.value);
                     break;
                 case SHORT:
-                    out.writeInt(((Short)this.value).shortValue());
+                    out.writeInt((Short) this.value);
                     break;
                 case INT:
-                    out.writeInt(((Integer)this.value).intValue());
+                    out.writeInt((Integer) this.value);
                     break;
                 case LONG:
-                    out.writeLong(((Long)this.value).longValue());
+                    out.writeLong((Long) this.value);
                     break;
                 case FLOAT:
-                    out.writeFloat(((Float)this.value).floatValue());
+                    out.writeFloat((Float) this.value);
                     break;
                 case DOUBLE:
-                    out.writeDouble(((Double)this.value).doubleValue());
+                    out.writeDouble((Double) this.value);
                     break;
                 case CHAR:
                     out.writeInt((int)((Character)this.value).charValue());
@@ -430,7 +423,7 @@
             }
 
             Class klass = view.getClass();
-            Method method = null;
+            Method method;
             try {
                 method = klass.getMethod(this.methodName, getParameterType());
             }
@@ -446,6 +439,7 @@
             }
 
             try {
+                //noinspection ConstantIfStatement
                 if (false) {
                     Log.d("RemoteViews", "view: " + klass.getName() + " calling method: "
                         + this.methodName + "(" + param.getName() + ") with "
@@ -816,13 +810,12 @@
      * @return The inflated view hierarchy
      */
     public View apply(Context context, ViewGroup parent) {
-        View result = null;
+        View result;
 
         Context c = prepareContext(context);
 
-        Resources r = c.getResources();
-        LayoutInflater inflater = (LayoutInflater) c
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        LayoutInflater inflater = (LayoutInflater)
+                c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 
         inflater = inflater.cloneInContext(c);
         inflater.setFilter(this);
@@ -858,7 +851,7 @@
     }
 
     private Context prepareContext(Context context) {
-        Context c = null;
+        Context c;
         String packageName = mPackage;
 
         if (packageName != null) {
@@ -872,8 +865,6 @@
             c = context;
         }
 
-        mContext = c;
-
         return c;
     }
 
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 075caae..0420918 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -171,6 +171,10 @@
     // VM pointer will be NULL if object is released
     Mutex::Autolock _l(mLock);
     JNIEnv *env = AndroidRuntime::getJNIEnv();
+    if (mCameraJObjectWeak == NULL) {
+        LOGW("callback on dead camera object");
+        return;
+    }
 
     // return data based on callback type
     switch(msgType) {
diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml
index 4669104..4f016bc 100644
--- a/core/res/res/drawable/progress_large.xml
+++ b/core/res/res/drawable/progress_large.xml
@@ -1,45 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
+<!--
+/*
+**
+** Copyright 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.
+*/
 -->
-
-    
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3"
-            android:thicknessRatio="8"
-            android:useLevel="false">
-
-        <size
-                android:width="76dip"
-                android:height="76dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#4c737373"
-                android:centerColor="#4c737373"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
-    
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_76"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_large_white.xml b/core/res/res/drawable/progress_large_white.xml
new file mode 100644
index 0000000..c690ed4
--- /dev/null
+++ b/core/res/res/drawable/progress_large_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_76"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml
index 92aebb5..eb1bd50 100644
--- a/core/res/res/drawable/progress_medium.xml
+++ b/core/res/res/drawable/progress_medium.xml
@@ -1,43 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
+<!--
+/*
+**
+** Copyright 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.
+*/
 -->
-
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3"
-            android:thicknessRatio="8"
-            android:useLevel="false">
-
-        <size
-                android:width="48dip"
-                android:height="48dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#4c737373"
-                android:centerColor="#4c737373"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_48"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml
new file mode 100644
index 0000000..b4f9b31
--- /dev/null
+++ b/core/res/res/drawable/progress_medium_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_48"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml
index e5b0021..e0ee5e4 100644
--- a/core/res/res/drawable/progress_small.xml
+++ b/core/res/res/drawable/progress_small.xml
@@ -1,45 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
+<!--
+/*
+**
+** Copyright 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.
+*/
 -->
-
- 
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-
-    <!-- An extra pixel is added on both ratios for stroke -->        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3.2"
-            android:thicknessRatio="5.333"
-            android:useLevel="false">
-
-        <size
-                android:width="16dip"
-                android:height="16dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#4c737373"
-                android:centerColor="#4c737373"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_black_16"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml
index cf8e41cb..8cfba86 100644
--- a/core/res/res/drawable/progress_small_titlebar.xml
+++ b/core/res/res/drawable/progress_small_titlebar.xml
@@ -1,45 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
+<!--
+/*
+**
+** Copyright 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.
+*/
 -->
-
- 
-<rotate xmlns:android="http://schemas.android.com/apk/res/android"
-        android:pivotX="50%" android:pivotY="50%"
-        android:fromDegrees="0" android:toDegrees="360">
-
-    <!-- An extra pixel is added on both ratios for stroke -->        
-    <shape
-            android:shape="ring"
-            android:innerRadiusRatio="3.2"
-            android:thicknessRatio="5.333"
-            android:useLevel="false">
-
-        <size
-                android:width="16dip"
-                android:height="16dip"
-        />
-        
-        <gradient
-                android:type="sweep"
-                android:useLevel="false"
-                android:startColor="#ff666666"
-                android:centerColor="#ff666666"
-                android:centerY="0.50"
-                android:endColor="#ffffd300"
-        />
-        
-    </shape>
-    
-</rotate>
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_16"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml
new file mode 100644
index 0000000..8cfba86
--- /dev/null
+++ b/core/res/res/drawable/progress_small_white.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/spinner_white_16"
+    android:pivotX="50%"
+    android:pivotY="50%"
+    android:framesCount="12"
+    android:frameDuration="100" />
diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable/spinner_black_16.png
new file mode 100644
index 0000000..5ee33ce
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable/spinner_black_48.png
new file mode 100644
index 0000000..3a68192
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable/spinner_black_76.png
new file mode 100644
index 0000000..ec57460
--- /dev/null
+++ b/core/res/res/drawable/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_16.png b/core/res/res/drawable/spinner_white_16.png
new file mode 100644
index 0000000..dd2e1fd
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable/spinner_white_48.png
new file mode 100644
index 0000000..d25a33e
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable/spinner_white_76.png
new file mode 100644
index 0000000..f53e8ff
--- /dev/null
+++ b/core/res/res/drawable/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml
new file mode 100644
index 0000000..2d0db93
--- /dev/null
+++ b/core/res/res/values-da/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">januar</string>
+    <string name="month_long_standalone_february">februar</string>
+    <string name="month_long_standalone_march">marts</string>
+    <string name="month_long_standalone_april">april</string>
+    <string name="month_long_standalone_may">maj</string>
+    <string name="month_long_standalone_june">juni</string>
+    <string name="month_long_standalone_july">juli</string>
+    <string name="month_long_standalone_august">august</string>
+    <string name="month_long_standalone_september">september</string>
+    <string name="month_long_standalone_october">oktober</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">januar</string>
+    <string name="month_long_february">februar</string>
+    <string name="month_long_march">marts</string>
+    <string name="month_long_april">april</string>
+    <string name="month_long_may">maj</string>
+    <string name="month_long_june">juni</string>
+    <string name="month_long_july">juli</string>
+    <string name="month_long_august">august</string>
+    <string name="month_long_september">september</string>
+    <string name="month_long_october">oktober</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">jan.</string>
+    <string name="month_medium_february">feb.</string>
+    <string name="month_medium_march">mar.</string>
+    <string name="month_medium_april">apr.</string>
+    <string name="month_medium_may">maj</string>
+    <string name="month_medium_june">jun.</string>
+    <string name="month_medium_july">jul.</string>
+    <string name="month_medium_august">aug.</string>
+    <string name="month_medium_september">sep.</string>
+    <string name="month_medium_october">okt.</string>
+    <string name="month_medium_november">nov.</string>
+    <string name="month_medium_december">dec.</string>
+
+    <string name="month_shortest_january">J</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">J</string>
+    <string name="month_shortest_july">J</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="day_of_week_long_sunday">søndag</string>
+    <string name="day_of_week_long_monday">mandag</string>
+    <string name="day_of_week_long_tuesday">tirsdag</string>
+    <string name="day_of_week_long_wednesday">onsdag</string>
+    <string name="day_of_week_long_thursday">torsdag</string>
+    <string name="day_of_week_long_friday">fredag</string>
+    <string name="day_of_week_long_saturday">lørdag</string>
+
+    <string name="day_of_week_medium_sunday">søn</string>
+    <string name="day_of_week_medium_monday">man</string>
+    <string name="day_of_week_medium_tuesday">tir</string>
+    <string name="day_of_week_medium_wednesday">ons</string>
+    <string name="day_of_week_medium_thursday">tor</string>
+    <string name="day_of_week_medium_friday">fre</string>
+    <string name="day_of_week_medium_saturday">lør</string>
+
+    <string name="day_of_week_short_sunday">søn</string>
+    <string name="day_of_week_short_monday">man</string>
+    <string name="day_of_week_short_tuesday">tir</string>
+    <string name="day_of_week_short_wednesday">ons</string>
+    <string name="day_of_week_short_thursday">tor</string>
+    <string name="day_of_week_short_friday">fre</string>
+    <string name="day_of_week_short_saturday">lør</string>
+
+    <string name="day_of_week_shortest_sunday">S</string>
+    <string name="day_of_week_shortest_monday">M</string>
+    <string name="day_of_week_shortest_tuesday">T</string>
+    <string name="day_of_week_shortest_wednesday">O</string>
+    <string name="day_of_week_shortest_thursday">T</string>
+    <string name="day_of_week_shortest_friday">F</string>
+    <string name="day_of_week_shortest_saturday">L</string>
+
+    <string name="am">f.m.</string>
+    <string name="pm">e.m.</string>
+    <string name="yesterday">i går</string>
+    <string name="today">i dag</string>
+    <string name="tomorrow">i morgen</string>
+
+    <string name="hour_minute_24">%H.%M</string>
+    <string name="hour_minute_ampm">%-l.%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l.%M %^p</string>
+    <string name="twelve_hour_time_format">h.mm a</string>
+    <string name="twenty_four_hour_time_format">HH.mm</string>
+    <string name="numeric_date">%d/%m/%Y</string>
+    <string name="numeric_date_format">dd/MM/yyyy</string>
+    <string name="numeric_date_template">"%s/%s/%s"</string>
+    <string name="month_day_year">%-e. %b %Y</string>
+    <string name="time_of_day">%H.%M.%S</string>
+    <string name="date_and_time">%H.%M.%S %d/%m/%Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%d/%m/%Y</string>
+    <string name="month_day">%-e. %B</string>
+    <string name="month">%B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%-e. %b</string>
+    <string name="abbrev_month">%b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s - %6$s %8$s/%7$s</string>
+    <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s %3$s/%2$s/%4$s - %6$s %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s-%2$s-%4$s - %10$s %6$s. %8$s-%7$s-%9$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s. %3$s-%2$s - %10$s %6$s. %8$s-%7$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s den %2$s - %6$s %4$s den %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s den %2$s - %4$s den %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s den %3$s</string>
+    <string name="wday_date">%2$s den %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s - %10$s %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s. %3$s. %2$s %4$s - %10$s %6$s. %8$s. %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s. %3$s. %2$s %4$s - %6$s. %8$s. %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s - %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml
new file mode 100644
index 0000000..e8f02fb
--- /dev/null
+++ b/core/res/res/values-el/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">Ιανουάριος</string>
+    <string name="month_long_standalone_february">Φεβρουάριος</string>
+    <string name="month_long_standalone_march">Μάρτιος</string>
+    <string name="month_long_standalone_april">Απρίλιος</string>
+    <string name="month_long_standalone_may">Μάιος</string>
+    <string name="month_long_standalone_june">Ιούνιος</string>
+    <string name="month_long_standalone_july">Ιούλιος</string>
+    <string name="month_long_standalone_august">Αύγουστος</string>
+    <string name="month_long_standalone_september">Σεπτέμβριος</string>
+    <string name="month_long_standalone_october">Οκτώβριος</string>
+    <string name="month_long_standalone_november">Νοέμβριος</string>
+    <string name="month_long_standalone_december">Δεκέμβριος</string>
+
+    <string name="month_long_january">Ιανουαρίου</string>
+    <string name="month_long_february">Φεβρουαρίου</string>
+    <string name="month_long_march">Μαρτίου</string>
+    <string name="month_long_april">Απριλίου</string>
+    <string name="month_long_may">Μαΐου</string>
+    <string name="month_long_june">Ιουνίου</string>
+    <string name="month_long_july">Ιουλίου</string>
+    <string name="month_long_august">Αυγούστου</string>
+    <string name="month_long_september">Σεπτεμβρίου</string>
+    <string name="month_long_october">Οκτωβρίου</string>
+    <string name="month_long_november">Νοεμβρίου</string>
+    <string name="month_long_december">Δεκεμβρίου</string>
+
+    <string name="month_medium_january">Ιαν</string>
+    <string name="month_medium_february">Φεβ</string>
+    <string name="month_medium_march">Μαρ</string>
+    <string name="month_medium_april">Απρ</string>
+    <string name="month_medium_may">Μαϊ</string>
+    <string name="month_medium_june">Ιουν</string>
+    <string name="month_medium_july">Ιουλ</string>
+    <string name="month_medium_august">Αυγ</string>
+    <string name="month_medium_september">Σεπ</string>
+    <string name="month_medium_october">Οκτ</string>
+    <string name="month_medium_november">Νοε</string>
+    <string name="month_medium_december">Δεκ</string>
+
+    <string name="month_shortest_january">Ι</string>
+    <string name="month_shortest_february">Φ</string>
+    <string name="month_shortest_march">Μ</string>
+    <string name="month_shortest_april">Α</string>
+    <string name="month_shortest_may">Μ</string>
+    <string name="month_shortest_june">Ι</string>
+    <string name="month_shortest_july">Ι</string>
+    <string name="month_shortest_august">Α</string>
+    <string name="month_shortest_september">Σ</string>
+    <string name="month_shortest_october">Ο</string>
+    <string name="month_shortest_november">Ν</string>
+    <string name="month_shortest_december">Δ</string>
+
+    <string name="day_of_week_long_sunday">Κυριακή</string>
+    <string name="day_of_week_long_monday">Δευτέρα</string>
+    <string name="day_of_week_long_tuesday">Τρίτη</string>
+    <string name="day_of_week_long_wednesday">Τετάρτη</string>
+    <string name="day_of_week_long_thursday">Πέμπτη</string>
+    <string name="day_of_week_long_friday">Παρασκευή</string>
+    <string name="day_of_week_long_saturday">Σάββατο</string>
+
+    <string name="day_of_week_medium_sunday">Κυρ</string>
+    <string name="day_of_week_medium_monday">Δευ</string>
+    <string name="day_of_week_medium_tuesday">Τρι</string>
+    <string name="day_of_week_medium_wednesday">Τετ</string>
+    <string name="day_of_week_medium_thursday">Πεμ</string>
+    <string name="day_of_week_medium_friday">Παρ</string>
+    <string name="day_of_week_medium_saturday">Σαβ</string>
+
+    <string name="day_of_week_short_sunday">Κυρ</string>
+    <string name="day_of_week_short_monday">Δευ</string>
+    <string name="day_of_week_short_tuesday">Τρι</string>
+    <string name="day_of_week_short_wednesday">Τετ</string>
+    <string name="day_of_week_short_thursday">Πεμ</string>
+    <string name="day_of_week_short_friday">Παρ</string>
+    <string name="day_of_week_short_saturday">Σαβ</string>
+
+    <string name="day_of_week_shortest_sunday">Κ</string>
+    <string name="day_of_week_shortest_monday">Δ</string>
+    <string name="day_of_week_shortest_tuesday">Τ</string>
+    <string name="day_of_week_shortest_wednesday">Τ</string>
+    <string name="day_of_week_shortest_thursday">Π</string>
+    <string name="day_of_week_shortest_friday">Π</string>
+    <string name="day_of_week_shortest_saturday">Σ</string>
+
+    <string name="am">π.μ.</string>
+    <string name="pm">μ.μ.</string>
+    <string name="yesterday">Χτες</string>
+    <string name="today">Σήμερα</string>
+    <string name="tomorrow">Αύριο</string>
+
+    <string name="hour_minute_24">%-k:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H:mm</string>
+    <string name="numeric_date">%d/%m/%Y</string>
+    <string name="numeric_date_format">dd/MM/yyyy</string>
+    <string name="numeric_date_template">"%s/%s/%s"</string>
+    <string name="month_day_year">%d %B %Y</string>
+    <string name="time_of_day">%-l:%M:%S %p</string>
+    <string name="date_and_time">%-l:%M:%S %p %d %b %Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%d %b %Y</string>
+    <string name="month_day">%-e %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%-B %Y</string>
+    <string name="abbrev_month_day">%-e %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s/%2$s - %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s, %3$s/%2$s - %6$s, %8$s/%7$s</string>
+    <string name="numeric_mdy1_mdy2">%3$s/%2$s/%4$s - %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %3$s/%2$s/%4$s - %6$s, %8$s/%7$s/%9$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s/%2$s/%4$s - %10$s %6$s, %8$s/%7$s/%9$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s/%2$s - %10$s %6$s, %8$s/%7$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s/%2$s/%4$s - %10$s %8$s/%7$s/%9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s, %3$s</string>
+    <string name="wday_date">%2$s, %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s %2$s - %10$s %6$s, %8$s %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s %2$s %4$s - %10$s %6$s, %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s-%8$s %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s %2$s - %6$s, %8$s %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml
new file mode 100644
index 0000000..a6ffc9a
--- /dev/null
+++ b/core/res/res/values-sv/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">januari</string>
+    <string name="month_long_standalone_february">februari</string>
+    <string name="month_long_standalone_march">mars</string>
+    <string name="month_long_standalone_april">april</string>
+    <string name="month_long_standalone_may">maj</string>
+    <string name="month_long_standalone_june">juni</string>
+    <string name="month_long_standalone_july">juli</string>
+    <string name="month_long_standalone_august">augusti</string>
+    <string name="month_long_standalone_september">september</string>
+    <string name="month_long_standalone_october">oktober</string>
+    <string name="month_long_standalone_november">november</string>
+    <string name="month_long_standalone_december">december</string>
+
+    <string name="month_long_january">januari</string>
+    <string name="month_long_february">februari</string>
+    <string name="month_long_march">mars</string>
+    <string name="month_long_april">april</string>
+    <string name="month_long_may">maj</string>
+    <string name="month_long_june">juni</string>
+    <string name="month_long_july">juli</string>
+    <string name="month_long_august">augusti</string>
+    <string name="month_long_september">september</string>
+    <string name="month_long_october">oktober</string>
+    <string name="month_long_november">november</string>
+    <string name="month_long_december">december</string>
+
+    <string name="month_medium_january">jan</string>
+    <string name="month_medium_february">feb</string>
+    <string name="month_medium_march">mar</string>
+    <string name="month_medium_april">apr</string>
+    <string name="month_medium_may">maj</string>
+    <string name="month_medium_june">jun</string>
+    <string name="month_medium_july">jul</string>
+    <string name="month_medium_august">aug</string>
+    <string name="month_medium_september">sep</string>
+    <string name="month_medium_october">okt</string>
+    <string name="month_medium_november">nov</string>
+    <string name="month_medium_december">dec</string>
+
+    <string name="month_shortest_january">J</string>
+    <string name="month_shortest_february">F</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">A</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">J</string>
+    <string name="month_shortest_july">J</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">S</string>
+    <string name="month_shortest_october">O</string>
+    <string name="month_shortest_november">N</string>
+    <string name="month_shortest_december">D</string>
+
+    <string name="day_of_week_long_sunday">söndag</string>
+    <string name="day_of_week_long_monday">måndag</string>
+    <string name="day_of_week_long_tuesday">tisdag</string>
+    <string name="day_of_week_long_wednesday">onsdag</string>
+    <string name="day_of_week_long_thursday">torsdag</string>
+    <string name="day_of_week_long_friday">fredag</string>
+    <string name="day_of_week_long_saturday">lördag</string>
+
+    <string name="day_of_week_medium_sunday">sön</string>
+    <string name="day_of_week_medium_monday">mån</string>
+    <string name="day_of_week_medium_tuesday">tis</string>
+    <string name="day_of_week_medium_wednesday">ons</string>
+    <string name="day_of_week_medium_thursday">tors</string>
+    <string name="day_of_week_medium_friday">fre</string>
+    <string name="day_of_week_medium_saturday">lör</string>
+
+    <string name="day_of_week_short_sunday">sön</string>
+    <string name="day_of_week_short_monday">mån</string>
+    <string name="day_of_week_short_tuesday">tis</string>
+    <string name="day_of_week_short_wednesday">ons</string>
+    <string name="day_of_week_short_thursday">tors</string>
+    <string name="day_of_week_short_friday">fre</string>
+    <string name="day_of_week_short_saturday">lör</string>
+
+    <string name="day_of_week_shortest_sunday">S</string>
+    <string name="day_of_week_shortest_monday">M</string>
+    <string name="day_of_week_shortest_tuesday">T</string>
+    <string name="day_of_week_shortest_wednesday">O</string>
+    <string name="day_of_week_shortest_thursday">T</string>
+    <string name="day_of_week_shortest_friday">F</string>
+    <string name="day_of_week_shortest_saturday">L</string>
+
+    <string name="am">f.m.</string>
+    <string name="pm">e.m.</string>
+    <string name="yesterday">igår</string>
+    <string name="today">idag</string>
+    <string name="tomorrow">imorgon</string>
+
+    <string name="hour_minute_24">%-k.%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">H.mm</string>
+    <string name="numeric_date">%Y-%m-%d</string>
+    <string name="numeric_date_format">yyyy-MM-dd</string>
+    <string name="numeric_date_template">"%s-%s-%s"</string>
+    <string name="month_day_year">%-e %B %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%H:%M:%S %-e %b %Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%-e %b %Y</string>
+    <string name="month_day">%-e %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%Y %B</string>
+    <string name="abbrev_month_day">%-e %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%Y %b</string>
+    <string name="time1_time2">%1$s – %2$s</string>
+    <string name="date1_date2">%2$s – %5$s</string>
+    <string name="numeric_md1_md2">%3$s/%2$s – %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%1$s %3$s/%2$s – %6$s %8$s/%7$s</string>
+    <string name="numeric_mdy1_mdy2">%4$s-%2$s-%3$s – %9$s-%7$s-%8$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %4$s-%2$s-%3$s – %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s-%2$s-%3$s – %10$s %6$s, %9$s-%7$s-%8$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s – %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s/%2$s – %10$s %6$s %8$s/%7$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %4$s-%2$s-%3$s – %10$s %9$s-%7$s-%8$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s – %6$s %4$s %5$s</string>
+    <string name="wday1_date1_wday2_date2">%1$s %2$s – %4$s %5$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s – %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %2$s %3$s</string>
+    <string name="wday_date">%2$s %3$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s %2$s – %8$s %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s – %10$s %8$s %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %3$s %2$s – %10$s %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s – %10$s %8$s %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s %3$s %2$s %4$s – %10$s %6$s %8$s %7$s %9$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s %4$s – %6$s %8$s %7$s %9$s</string>
+    <string name="same_month_md1_md2">%3$s–%8$s %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%1$s %3$s %2$s – %6$s %8$s %7$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s %2$s–%8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s–%6$s %8$s %7$s %9$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml
new file mode 100644
index 0000000..d612305
--- /dev/null
+++ b/core/res/res/values-tr/donottranslate-cldr.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="month_long_standalone_january">Ocak</string>
+    <string name="month_long_standalone_february">Şubat</string>
+    <string name="month_long_standalone_march">Mart</string>
+    <string name="month_long_standalone_april">Nisan</string>
+    <string name="month_long_standalone_may">Mayıs</string>
+    <string name="month_long_standalone_june">Haziran</string>
+    <string name="month_long_standalone_july">Temmuz</string>
+    <string name="month_long_standalone_august">Ağustos</string>
+    <string name="month_long_standalone_september">Eylül</string>
+    <string name="month_long_standalone_october">Ekim</string>
+    <string name="month_long_standalone_november">Kasım</string>
+    <string name="month_long_standalone_december">Aralık</string>
+
+    <string name="month_long_january">Ocak</string>
+    <string name="month_long_february">Şubat</string>
+    <string name="month_long_march">Mart</string>
+    <string name="month_long_april">Nisan</string>
+    <string name="month_long_may">Mayıs</string>
+    <string name="month_long_june">Haziran</string>
+    <string name="month_long_july">Temmuz</string>
+    <string name="month_long_august">Ağustos</string>
+    <string name="month_long_september">Eylül</string>
+    <string name="month_long_october">Ekim</string>
+    <string name="month_long_november">Kasım</string>
+    <string name="month_long_december">Aralık</string>
+
+    <string name="month_medium_january">Oca</string>
+    <string name="month_medium_february">Şub</string>
+    <string name="month_medium_march">Mar</string>
+    <string name="month_medium_april">Nis</string>
+    <string name="month_medium_may">May</string>
+    <string name="month_medium_june">Haz</string>
+    <string name="month_medium_july">Tem</string>
+    <string name="month_medium_august">Ağu</string>
+    <string name="month_medium_september">Eyl</string>
+    <string name="month_medium_october">Eki</string>
+    <string name="month_medium_november">Kas</string>
+    <string name="month_medium_december">Ara</string>
+
+    <string name="month_shortest_january">O</string>
+    <string name="month_shortest_february">Ş</string>
+    <string name="month_shortest_march">M</string>
+    <string name="month_shortest_april">N</string>
+    <string name="month_shortest_may">M</string>
+    <string name="month_shortest_june">H</string>
+    <string name="month_shortest_july">T</string>
+    <string name="month_shortest_august">A</string>
+    <string name="month_shortest_september">E</string>
+    <string name="month_shortest_october">E</string>
+    <string name="month_shortest_november">K</string>
+    <string name="month_shortest_december">A</string>
+
+    <string name="day_of_week_long_sunday">Pazar</string>
+    <string name="day_of_week_long_monday">Pazartesi</string>
+    <string name="day_of_week_long_tuesday">Salı</string>
+    <string name="day_of_week_long_wednesday">Çarşamba</string>
+    <string name="day_of_week_long_thursday">Perşembe</string>
+    <string name="day_of_week_long_friday">Cuma</string>
+    <string name="day_of_week_long_saturday">Cumartesi</string>
+
+    <string name="day_of_week_medium_sunday">Paz</string>
+    <string name="day_of_week_medium_monday">Pzt</string>
+    <string name="day_of_week_medium_tuesday">Sal</string>
+    <string name="day_of_week_medium_wednesday">Çar</string>
+    <string name="day_of_week_medium_thursday">Per</string>
+    <string name="day_of_week_medium_friday">Cum</string>
+    <string name="day_of_week_medium_saturday">Cmt</string>
+
+    <string name="day_of_week_short_sunday">Paz</string>
+    <string name="day_of_week_short_monday">Pzt</string>
+    <string name="day_of_week_short_tuesday">Sal</string>
+    <string name="day_of_week_short_wednesday">Çar</string>
+    <string name="day_of_week_short_thursday">Per</string>
+    <string name="day_of_week_short_friday">Cum</string>
+    <string name="day_of_week_short_saturday">Cmt</string>
+
+    <string name="day_of_week_shortest_sunday">P</string>
+    <string name="day_of_week_shortest_monday">P</string>
+    <string name="day_of_week_shortest_tuesday">S</string>
+    <string name="day_of_week_shortest_wednesday">Ç</string>
+    <string name="day_of_week_shortest_thursday">P</string>
+    <string name="day_of_week_shortest_friday">C</string>
+    <string name="day_of_week_shortest_saturday">C</string>
+
+    <string name="am">AM</string>
+    <string name="pm">PM</string>
+    <string name="yesterday">Dün</string>
+    <string name="today">Bugün</string>
+    <string name="tomorrow">Yarın</string>
+
+    <string name="hour_minute_24">%H:%M</string>
+    <string name="hour_minute_ampm">%-l:%M %p</string>
+    <string name="hour_minute_cap_ampm">%-l:%M %^p</string>
+    <string name="twelve_hour_time_format">h:mm a</string>
+    <string name="twenty_four_hour_time_format">HH:mm</string>
+    <string name="numeric_date">%d %m %Y</string>
+    <string name="numeric_date_format">dd MM yyyy</string>
+    <string name="numeric_date_template">"%s %s %s"</string>
+    <string name="month_day_year">%d %B %Y</string>
+    <string name="time_of_day">%H:%M:%S</string>
+    <string name="date_and_time">%H:%M:%S %d %b %Y</string>
+    <string name="date_time">%2$s %1$s</string>
+    <string name="time_date">%1$s %3$s</string>
+    <string name="abbrev_month_day_year">%d %b %Y</string>
+    <string name="month_day">%d %B</string>
+    <string name="month">%-B</string>
+    <string name="month_year">%B %Y</string>
+    <string name="abbrev_month_day">%d %b</string>
+    <string name="abbrev_month">%-b</string>
+    <string name="abbrev_month_year">%b %Y</string>
+    <string name="time1_time2">%1$s - %2$s</string>
+    <string name="date1_date2">%2$s - %5$s</string>
+    <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string>
+    <string name="numeric_wday1_md1_wday2_md2">%3$s.%2$s %1$s - %8$s.%7$s %6$s</string>
+    <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string>
+    <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s %1$s - %8$s.%7$s.%9$s %6$s</string>
+    <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s.%2$s.%4$s %1$s - %10$s %8$s.%7$s.%9$s %6$s</string>
+    <string name="numeric_md1_time1_md2_time2">%5$s %3$s/%2$s - %10$s %8$s/%7$s</string>
+    <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %3$s/%2$s %1$s - %10$s %8$s/%7$s %6$s</string>
+    <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s %1$s - %6$s %5$s %4$s</string>
+    <string name="wday1_date1_wday2_date2">%2$s %1$s - %5$s %4$s</string>
+    <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
+    <string name="time_wday_date">%1$s %3$s %2$s</string>
+    <string name="wday_date">%3$s %2$s</string>
+    <string name="time_wday">%1$s %2$s</string>
+    <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string>
+    <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string>
+    <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
+    <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string>
+    <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s %1$s - %10$s %8$s %7$s %6$s</string>
+    <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
+    <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string>
+    <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s %1$s - %10$s %8$s %7$s %9$s %6$s</string>
+    <string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s %1$s - %8$s %7$s %9$s %6$s</string>
+    <string name="same_month_md1_md2">%3$s-%8$s %2$s</string>
+    <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s %1$s - %8$s %7$s %6$s</string>
+    <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
+    <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
+    <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s %1$s - %8$s %7$s y %6$s</string>
+    <string name="short_format_month">%b</string>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 30fee0c..b4d9172 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -350,6 +350,12 @@
         <attr name="progressBarStyleSmallTitle" format="reference" />
         <!-- Large ProgressBar style. This is a large circular progress bar. -->
         <attr name="progressBarStyleLarge" format="reference" />
+        <!-- Inverse ProgressBar style. This is a medium circular progress bar. -->
+        <attr name="progressBarStyleInverse" format="reference" />
+        <!-- Small inverse ProgressBar style. This is a small circular progress bar. -->
+        <attr name="progressBarStyleSmallInverse" format="reference" />
+        <!-- Large inverse ProgressBar style. This is a large circular progress bar. -->
+        <attr name="progressBarStyleLargeInverse" format="reference" /> 
         <!-- Default SeekBar style. -->
         <attr name="seekBarStyle" format="reference" />
         <!-- Default RatingBar style. -->
@@ -2873,6 +2879,11 @@
              results for "bo", it would not be queried again for "bob".
              The default value is <code>false</code>. <i>Optional attribute.</i>. -->
         <attr name="queryAfterZeroResults" format="boolean" />
+        
+        <!-- If provided, this string will be used to describe the searchable item in the
+             searchable items settings within system search settings. <i>Optional
+             attribute.</i> -->
+        <attr name="searchSettingsDescription" format="string" />
 
     </declare-styleable>
 
@@ -3335,5 +3346,18 @@
         <attr name="accountType"/>
     </declare-styleable>
 
+    <!-- =============================== -->
+    <!-- Contacts meta-data attributes -->
+    <!-- =============================== -->
+
+    <declare-styleable name="Icon">
+        <attr name="icon" />
+        <attr name="mimeType" />
+    </declare-styleable>
+
+    <declare-styleable name="IconDefault">
+        <attr name="icon" />
+    </declare-styleable>
+
 </resources>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6727768..7af49a6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1120,11 +1120,18 @@
   <public type="attr" name="smallScreens" />
   <public type="attr" name="normalScreens" />
   <public type="attr" name="largeScreens" />
+  <public type="attr" name="progressBarStyleInverse" />
+  <public type="attr" name="progressBarStyleSmallInverse" />
+  <public type="attr" name="progressBarStyleLargeInverse" /> 
+  <public type="attr" name="searchSettingsDescription" />
 
   <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
 
   <public-padding type="id" name="donut_resource_pad" end="0x01020040" />
   
+  <public type="style" name="Widget.ProgressBar.Inverse" id="0x0103005b" />
+  <public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" />
+  <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" /> 
   <public-padding type="style" name="donut_resource_pad" end="0x01030070" />
   
   <public-padding type="string" name="donut_resource_pad" end="0x01040030" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 612e6f4..eeafbe6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1790,6 +1790,11 @@
     <!-- See EXTMEDIA_FORMAT.    This is the button text to format the sd card. -->
     <string name="extmedia_format_button_format">Format</string>
 
+    <!-- Title of notification shown when ADB is actively connected to the phone. -->
+    <string name="adb_active_notification_title">USB debugging connected</string>
+    <!-- Message of notification shown when ADB is actively connected to the phone. -->
+    <string name="adb_active_notification_message">A computer is connected to your phone.</string>
+    
     <!-- Used to replace %s in urls retreived from the signin server with locales.  For Some        -->
     <!-- devices we don't support all the locales we ship to and need to replace the '%s' with a    -->
     <!-- locale string based on mcc values.  By default (0-length string) we don't replace the %s   -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 648a7dd..7d235ec 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -243,7 +243,7 @@
 
     <style name="Widget.ProgressBar">
         <item name="android:indeterminateOnly">true</item>
-        <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item>
+        <item name="android:indeterminateDrawable">@android:drawable/progress_medium_white</item>
         <item name="android:indeterminateBehavior">repeat</item>
         <item name="android:indeterminateDuration">3500</item>
         <item name="android:minWidth">48dip</item>
@@ -253,7 +253,7 @@
     </style>
 
     <style name="Widget.ProgressBar.Large">
-        <item name="android:indeterminateDrawable">@android:drawable/progress_large</item>
+        <item name="android:indeterminateDrawable">@android:drawable/progress_large_white</item>
         <item name="android:minWidth">76dip</item>
         <item name="android:maxWidth">76dip</item>
         <item name="android:minHeight">76dip</item>
@@ -261,13 +261,25 @@
     </style>
     
     <style name="Widget.ProgressBar.Small">
-        <item name="android:indeterminateDrawable">@android:drawable/progress_small</item>
+        <item name="android:indeterminateDrawable">@android:drawable/progress_small_white</item>
         <item name="android:minWidth">16dip</item>
         <item name="android:maxWidth">16dip</item>
         <item name="android:minHeight">16dip</item>
         <item name="android:maxHeight">16dip</item>
     </style>
 
+    <style name="Widget.ProgressBar.Inverse">
+        <item name="android:indeterminateDrawable">@android:drawable/progress_medium</item>
+    </style>
+
+    <style name="Widget.ProgressBar.Large.Inverse">
+        <item name="android:indeterminateDrawable">@android:drawable/progress_large</item>
+    </style>
+
+    <style name="Widget.ProgressBar.Small.Inverse">
+        <item name="android:indeterminateDrawable">@android:drawable/progress_small</item>
+    </style> 
+    
     <style name="Widget.ProgressBar.Small.Title">
         <item name="android:indeterminateDrawable">@android:drawable/progress_small_titlebar</item>
     </style>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f37d514..bd6e1df 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -154,6 +154,9 @@
         <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item>
         <item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
         <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
+        <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
+		<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+	    <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item> 
         <item name="seekBarStyle">@android:style/Widget.SeekBar</item>
         <item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
         <item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -233,6 +236,13 @@
         <item name="listViewStyle">@android:style/Widget.ListView.White</item>
         <item name="listDivider">@drawable/divider_horizontal_bright</item>
         <item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
+        
+        <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
+		<item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
+		<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
+		<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
+		<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
+		<item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item> 
     </style>
     
     <!-- Variant of the light theme with no title bar -->
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 2b24ef2..778c903 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -57,7 +57,9 @@
         mBitmap = patch.mBitmap;
         mChunk = patch.mChunk;
         mSrcName = patch.mSrcName;
-        mPaint = new Paint(patch.mPaint);
+        if (patch.mPaint != null) {
+            mPaint = new Paint(patch.mPaint);
+        }
         validateNinePatchChunk(mBitmap.ni(), mChunk);
     }
 
@@ -120,7 +122,6 @@
     
     public native static boolean isNinePatchChunk(byte[] chunk);
 
-    private final Rect   mRect = new Rect();
     private final Bitmap mBitmap;
     private final byte[] mChunk;
     private Paint        mPaint;
diff --git a/graphics/java/android/graphics/drawable/Animatable.java b/graphics/java/android/graphics/drawable/Animatable.java
new file mode 100644
index 0000000..9dc62c3
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/Animatable.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.graphics.drawable;
+
+/**
+ * Interface that drawables suporting animations should implement.
+ */
+public interface Animatable {
+    /**
+     * Starts the drawable's animation.
+     */
+    void start();
+
+    /**
+     * Stops the drawable's animation.
+     */
+    void stop();
+
+    /**
+     * Indicates whether the animation is running.
+     * 
+     * @return True if the animation is running, false otherwise.
+     */
+    boolean isRunning();
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index 08d295d..ac96f20 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -35,11 +35,14 @@
 /**
  * @hide
  */
-public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable {
+public class AnimatedRotateDrawable extends Drawable implements Drawable.Callback, Runnable,
+        Animatable {
+
     private AnimatedRotateState mState;
     private boolean mMutated;
     private float mCurrentDegrees;
     private float mIncrement;
+    private boolean mRunning;
 
     public AnimatedRotateDrawable() {
         this(null);
@@ -80,10 +83,24 @@
         drawable.draw(canvas);
 
         canvas.restoreToCount(saveCount);
-        
-        nextFrame();
     }
-    
+
+    public void start() {
+        if (!mRunning) {
+            mRunning = true;
+            nextFrame();
+        }
+    }
+
+    public void stop() {
+        mRunning = false;
+        unscheduleSelf(this);
+    }
+
+    public boolean isRunning() {
+        return mRunning;
+    }
+
     private void nextFrame() {
         unscheduleSelf(this);
         scheduleSelf(this, SystemClock.uptimeMillis() + mState.mFrameDuration);
@@ -96,8 +113,8 @@
         if (mCurrentDegrees > (360.0f - mIncrement)) {
             mCurrentDegrees = 0.0f;
         }
-        nextFrame();
         invalidateSelf();
+        nextFrame();
     }
     
     @Override
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index bab1703..68718c9 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -71,7 +71,7 @@
  * @attr ref android.R.styleable#AnimationDrawableItem_duration
  * @attr ref android.R.styleable#AnimationDrawableItem_drawable
  */
-public class AnimationDrawable extends DrawableContainer implements Runnable {
+public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable {
     private final AnimationState mAnimationState;
     private int mCurFrame = -1;
     private boolean mMutated;
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 5c41ead..eb4151a 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1441,7 +1441,7 @@
  * This is the beginning of information about an entry in the resource
  * table.  It holds the reference to the name of this entry, and is
  * immediately followed by one of:
- *   * A Res_value structures, if FLAG_COMPLEX is -not- set.
+ *   * A Res_value structure, if FLAG_COMPLEX is -not- set.
  *   * An array of ResTable_map structures, if FLAG_COMPLEX is set.
  *     These supply a set of name/value mappings of data.
  */
@@ -1843,6 +1843,8 @@
     status_t parsePackage(
         const ResTable_package* const pkg, const Header* const header);
 
+    void print_value(const Package* pkg, const Res_value& value) const;
+    
     mutable Mutex               mLock;
 
     status_t                    mError;
diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java
new file mode 100644
index 0000000..1dc575b
--- /dev/null
+++ b/keystore/java/android/security/CertTool.java
@@ -0,0 +1,143 @@
+/*
+ * 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 android.security;
+
+import android.content.Context;
+import android.content.Intent;
+import android.security.Keystore;
+import android.text.TextUtils;
+
+
+/**
+ * The CertTool class provides the functions to list the certs/keys,
+ * generate the certificate request(csr) and store the certificate into
+ * keystore.
+ *
+ * {@hide}
+ */
+public class CertTool {
+    public static final String ACTION_ADD_CREDENTIAL =
+            "android.security.ADD_CREDENTIAL";
+    public static final String KEY_TYPE_NAME = "typeName";
+    public static final String KEY_ITEM = "item";
+    public static final String KEY_NAMESPACE = "namespace";
+    public static final String KEY_DESCRIPTION = "description";
+
+    private static final String TAG = "CertTool";
+
+    private static final String TITLE_CA_CERT = "CA Certificate";
+    private static final String TITLE_USER_CERT = "User Certificate";
+    private static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
+    private static final String TITLE_PRIVATE_KEY = "Private Key";
+    private static final String UNKNOWN = "Unknown";
+    private static final String ISSUER_NAME = "Issuer Name:";
+    private static final String DISTINCT_NAME = "Distinct Name:";
+
+    private static final String CA_CERTIFICATE = "CACERT";
+    private static final String USER_CERTIFICATE = "USRCERT";
+    private static final String USER_KEY = "USRKEY";
+
+    private static final String KEYNAME_DELIMITER = "_";
+    private static final Keystore keystore = Keystore.getInstance();
+
+    private native String generateCertificateRequest(int bits, String subject);
+    private native boolean isPkcs12Keystore(byte[] data);
+    private native int generateX509Certificate(byte[] data);
+    private native boolean isCaCertificate(int handle);
+    private native String getIssuerDN(int handle);
+    private native String getCertificateDN(int handle);
+    private native String getPrivateKeyPEM(int handle);
+    private native void freeX509Certificate(int handle);
+
+    public String getUserPrivateKey(String key) {
+        return USER_KEY + KEYNAME_DELIMITER + key;
+    }
+
+    public String getUserCertificate(String key) {
+        return USER_CERTIFICATE + KEYNAME_DELIMITER + key;
+    }
+
+    public String getCaCertificate(String key) {
+        return CA_CERTIFICATE + KEYNAME_DELIMITER + key;
+    }
+
+    public String[] getAllUserCertificateKeys() {
+        return keystore.listKeys(USER_KEY);
+    }
+
+    public String[] getAllCaCertificateKeys() {
+        return keystore.listKeys(CA_CERTIFICATE);
+    }
+
+    public String[] getSupportedKeyStrenghs() {
+        return new String[] {"High Grade", "Medium Grade"};
+    }
+
+    private int getKeyLength(int index) {
+        if (index == 0) return 2048;
+        return 1024;
+    }
+
+    public String generateKeyPair(int keyStrengthIndex, String challenge,
+            String dirName) {
+        return generateCertificateRequest(getKeyLength(keyStrengthIndex),
+                dirName);
+    }
+
+    private Intent prepareIntent(String title, byte[] data, String namespace,
+            String issuer, String distinctName) {
+        Intent intent = new Intent(ACTION_ADD_CREDENTIAL);
+        intent.putExtra(KEY_TYPE_NAME, title);
+        intent.putExtra(KEY_ITEM + "0", data);
+        intent.putExtra(KEY_NAMESPACE + "0", namespace);
+        intent.putExtra(KEY_DESCRIPTION + "0", ISSUER_NAME + issuer);
+        intent.putExtra(KEY_DESCRIPTION + "1", DISTINCT_NAME + distinctName);
+        return intent;
+    }
+
+    private void addExtraIntentInfo(Intent intent, String namespace,
+            String data) {
+        intent.putExtra(KEY_ITEM + "1", data);
+        intent.putExtra(KEY_NAMESPACE + "1", namespace);
+    }
+
+    public synchronized void addCertificate(byte[] data, Context context) {
+        int handle;
+        Intent intent = null;
+
+        if (isPkcs12Keystore(data)) {
+            intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY,
+                    UNKNOWN, UNKNOWN);
+        } else if ((handle = generateX509Certificate(data)) != 0) {
+            String issuer = getIssuerDN(handle);
+            String distinctName = getCertificateDN(handle);
+            String privateKeyPEM = getPrivateKeyPEM(handle);
+            if (isCaCertificate(handle)) {
+                intent = prepareIntent(TITLE_CA_CERT, data, CA_CERTIFICATE,
+                        issuer, distinctName);
+            } else {
+                intent = prepareIntent(TITLE_USER_CERT, data, USER_CERTIFICATE,
+                        issuer, distinctName);
+                if (!TextUtils.isEmpty(privateKeyPEM)) {
+                    addExtraIntentInfo(intent, USER_KEY, privateKeyPEM);
+                }
+            }
+            freeX509Certificate(handle);
+        }
+        if (intent != null) context.startActivity(intent);
+    }
+}
diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java
index 2a3e6a7..1f14da7 100644
--- a/keystore/java/android/security/Keystore.java
+++ b/keystore/java/android/security/Keystore.java
@@ -20,35 +20,35 @@
  * The Keystore class provides the functions to list the certs/keys in keystore.
  * {@hide}
  */
+
 public abstract class Keystore {
     private static final String TAG = "Keystore";
     private static final String[] NOTFOUND = new String[0];
 
+    // Keystore States
+    public static final int BOOTUP = 0;
+    public static final int UNINITIALIZED = 1;
+    public static final int LOCKED = 2;
+    public static final int UNLOCKED = 3;
+
     /**
      */
     public static Keystore getInstance() {
         return new FileKeystore();
     }
 
-    // for compatiblity, start from here
-    /**
-     */
-    public abstract String getUserkey(String key);
+    public abstract int lock();
+    public abstract int unlock(String password);
+    public abstract int getState();
+    public abstract int changePassword(String oldPassword, String newPassword);
+    public abstract int setPassword(String firstPassword);
+    public abstract String[] listKeys(String namespace);
+    public abstract int put(String namespace, String keyname, String value);
+    public abstract String get(String namespace, String keyname);
+    public abstract int remove(String namespace, String keyname);
+    public abstract int reset();
 
-    /**
-     */
-    public abstract String getCertificate(String key);
-
-    /**
-     */
-    public abstract String[] getAllCertificateKeys();
-
-    /**
-     */
-    public abstract String[] getAllUserkeyKeys();
-
-    // to here
-
+    // TODO: for migrating to the mini-keystore, clean up from here
     /**
      */
     public abstract String getCaCertificate(String key);
@@ -89,101 +89,41 @@
             int keyStrengthIndex, String challenge, String organizations);
 
     public abstract void addCertificate(byte[] cert);
+    // to here
 
     private static class FileKeystore extends Keystore {
         private static final String SERVICE_NAME = "keystore";
-        private static final String LIST_CA_CERTIFICATES = "listcacerts";
-        private static final String LIST_USER_CERTIFICATES = "listusercerts";
-        private static final String GET_CA_CERTIFICATE = "getcacert";
-        private static final String GET_USER_CERTIFICATE = "getusercert";
-        private static final String GET_USER_KEY = "getuserkey";
-        private static final String ADD_CA_CERTIFICATE = "addcacert";
-        private static final String ADD_USER_CERTIFICATE = "addusercert";
-        private static final String ADD_USER_KEY = "adduserkey";
-        private static final String COMMAND_DELIMITER = "\t";
+        private static final String CA_CERTIFICATE = "CaCertificate";
+        private static final String USER_CERTIFICATE = "UserCertificate";
+        private static final String USER_KEY = "UserPrivateKey";
+        private static final String COMMAND_DELIMITER = " ";
         private static final ServiceCommand mServiceCommand =
                 new ServiceCommand(SERVICE_NAME);
 
-        // for compatiblity, start from here
-
-        private static final String LIST_CERTIFICATES = "listcerts";
-        private static final String LIST_USERKEYS = "listuserkeys";
-        private static final String PATH = "/data/misc/keystore/";
-        private static final String USERKEY_PATH = PATH + "userkeys/";
-        private static final String CERT_PATH = PATH + "certs/";
-
-        @Override
-        public String getUserkey(String key) {
-            return USERKEY_PATH + key;
-        }
-
-        @Override
-        public String getCertificate(String key) {
-            return CERT_PATH + key;
-        }
-
-        @Override
-        public String[] getAllCertificateKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_CERTIFICATES);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
-        }
-
-        @Override
-        public String[] getAllUserkeyKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_USERKEYS);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
-        }
-
-        // to here
-
+        // TODO: for migrating to the mini-keystore, start from here
         @Override
         public String getUserPrivateKey(String key) {
-            return mServiceCommand.execute(
-                    GET_USER_KEY + COMMAND_DELIMITER + key);
+            return "";
         }
 
         @Override
         public String getUserCertificate(String key) {
-            return mServiceCommand.execute(
-                    GET_USER_CERTIFICATE + COMMAND_DELIMITER + key);
+            return "";
         }
 
         @Override
         public String getCaCertificate(String key) {
-            return mServiceCommand.execute(
-                    GET_CA_CERTIFICATE + COMMAND_DELIMITER + key);
+            return "";
         }
 
         @Override
         public String[] getAllUserCertificateKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_USER_CERTIFICATES);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
+            return new String[0];
         }
 
         @Override
         public String[] getAllCaCertificateKeys() {
-            try {
-                String result = mServiceCommand.execute(LIST_CA_CERTIFICATES);
-                if (result != null) return result.split("\\s+");
-                return NOTFOUND;
-            } catch (NumberFormatException ex) {
-                return NOTFOUND;
-            }
+          return new String[0];
         }
 
         @Override
@@ -221,25 +161,79 @@
             // TODO: real implementation
         }
 
-        private boolean addUserCertificate(String key, String certificate,
-                String privateKey) {
-            if(mServiceCommand.execute(ADD_USER_CERTIFICATE + COMMAND_DELIMITER
-                    + key + COMMAND_DELIMITER + certificate) != null) {
-                if (mServiceCommand.execute(ADD_USER_KEY + COMMAND_DELIMITER
-                        + key + COMMAND_DELIMITER + privateKey) != null) {
-                    return true;
-                }
-            }
-            return false;
+        // to here
+
+        @Override
+        public int lock() {
+            Reply result = mServiceCommand.execute(ServiceCommand.LOCK, null);
+            return (result != null) ? result.returnCode : -1;
         }
 
-        private boolean addCaCertificate(String key, String content) {
-            if (mServiceCommand.execute(ADD_CA_CERTIFICATE + COMMAND_DELIMITER
-                    + key + COMMAND_DELIMITER + content) != null) {
-                return true;
-            }
-            return false;
+        @Override
+        public int unlock(String password) {
+            Reply result = mServiceCommand.execute(ServiceCommand.UNLOCK,
+                    password);
+            return (result != null) ? result.returnCode : -1;
         }
 
+        @Override
+        public int getState() {
+            Reply result = mServiceCommand.execute(ServiceCommand.GET_STATE,
+                    null);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public int changePassword(String oldPassword, String newPassword) {
+            Reply result = mServiceCommand.execute(ServiceCommand.PASSWD,
+                    oldPassword + " " + newPassword);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public int setPassword(String firstPassword) {
+            Reply result = mServiceCommand.execute(ServiceCommand.PASSWD,
+                    firstPassword);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public String[] listKeys(String namespace) {
+            Reply result = mServiceCommand.execute(ServiceCommand.LIST_KEYS,
+                    namespace);
+            if ((result == null) || (result.returnCode != 0) ||
+                    (result.len == 0)) {
+                return NOTFOUND;
+            }
+            return new String(result.data, 0, result.len).split("\\s+");
+        }
+
+        @Override
+        public int put(String namespace, String keyname, String value) {
+            Reply result = mServiceCommand.execute(ServiceCommand.PUT_KEY,
+                    namespace + " " + keyname + " " + value);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public String get(String namespace, String keyname) {
+            Reply result = mServiceCommand.execute(ServiceCommand.GET_KEY,
+                    namespace + " " + keyname);
+            return (result != null) ? ((result.returnCode != 0) ? null :
+                    new String(result.data, 0, result.len)) : null;
+        }
+
+        @Override
+        public int remove(String namespace, String keyname) {
+            Reply result = mServiceCommand.execute(ServiceCommand.REMOVE_KEY,
+                    namespace + " " + keyname);
+            return (result != null) ? result.returnCode : -1;
+        }
+
+        @Override
+        public int reset() {
+            Reply result = mServiceCommand.execute(ServiceCommand.RESET, null);
+            return (result != null) ? result.returnCode : -1;
+        }
     }
 }
diff --git a/keystore/java/android/security/Reply.java b/keystore/java/android/security/Reply.java
new file mode 100644
index 0000000..15a0dde
--- /dev/null
+++ b/keystore/java/android/security/Reply.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.security;
+
+/*
+ * {@hide}
+ */
+public class Reply {
+    public int len;
+    public int returnCode;
+    public byte[] data = new byte[ServiceCommand.BUFFER_LENGTH];
+}
diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java
index f1d4302..2f335be 100644
--- a/keystore/java/android/security/ServiceCommand.java
+++ b/keystore/java/android/security/ServiceCommand.java
@@ -35,15 +35,25 @@
     public static final String SUCCESS = "0";
     public static final String FAILED = "-1";
 
+    // Opcodes for keystore commands.
+    public static final int LOCK = 0;
+    public static final int UNLOCK = 1;
+    public static final int PASSWD = 2;
+    public static final int GET_STATE = 3;
+    public static final int LIST_KEYS = 4;
+    public static final int GET_KEY = 5;
+    public static final int PUT_KEY = 6;
+    public static final int REMOVE_KEY = 7;
+    public static final int RESET = 8;
+    public static final int MAX_CMD_INDEX = 9;
+
+    public static final int BUFFER_LENGTH = 4096;
+
     private String mServiceName;
     private String mTag;
     private InputStream mIn;
     private OutputStream mOut;
     private LocalSocket mSocket;
-    private static final int BUFFER_LENGTH = 1024;
-
-    private byte buf[] = new byte[BUFFER_LENGTH];
-    private int buflen = 0;
 
     private boolean connect() {
         if (mSocket != null) {
@@ -104,35 +114,47 @@
         return false;
     }
 
-    private boolean readReply() {
-        int len, ret;
-        buflen = 0;
+    private Reply readReply() {
+        byte buf[] = new byte[4];
+        Reply reply = new Reply();
 
-        if (!readBytes(buf, 2)) return false;
-        ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if (ret != 0) return false;
+        if (!readBytes(buf, 4)) return null;
+        reply.len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8) |
+                ((((int) buf[2]) & 0xff) << 16) |
+                ((((int) buf[3]) & 0xff) << 24);
 
-        if (!readBytes(buf, 2)) return false;
-        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if (len > BUFFER_LENGTH) {
-            Log.e(mTag,"invalid reply length (" + len + ")");
+        if (!readBytes(buf, 4)) return null;
+        reply.returnCode = (((int) buf[0]) & 0xff) |
+                ((((int) buf[1]) & 0xff) << 8) |
+                ((((int) buf[2]) & 0xff) << 16) |
+                ((((int) buf[3]) & 0xff) << 24);
+
+        if (reply.len > BUFFER_LENGTH) {
+            Log.e(mTag,"invalid reply length (" + reply.len + ")");
             disconnect();
-            return false;
+            return null;
         }
-        if (!readBytes(buf, len)) return false;
-        buflen = len;
-        return true;
+        if (!readBytes(reply.data, reply.len)) return null;
+        return reply;
     }
 
-    private boolean writeCommand(String _cmd) {
-        byte[] cmd = _cmd.getBytes();
-        int len = cmd.length;
-        if ((len < 1) || (len > BUFFER_LENGTH)) return false;
+    private boolean writeCommand(int cmd, String _data) {
+        byte buf[] = new byte[8];
+        byte[] data = _data.getBytes();
+        int len = data.length;
+        // the length of data
         buf[0] = (byte) (len & 0xff);
         buf[1] = (byte) ((len >> 8) & 0xff);
+        buf[2] = (byte) ((len >> 16) & 0xff);
+        buf[3] = (byte) ((len >> 24) & 0xff);
+        // the opcode of the command
+        buf[4] = (byte) (cmd & 0xff);
+        buf[5] = (byte) ((cmd >> 8) & 0xff);
+        buf[6] = (byte) ((cmd >> 16) & 0xff);
+        buf[7] = (byte) ((cmd >> 24) & 0xff);
         try {
-            mOut.write(buf, 0, 2);
-            mOut.write(cmd, 0, len);
+            mOut.write(buf, 0, 8);
+            mOut.write(data, 0, len);
         } catch (IOException ex) {
             Log.e(mTag,"write error");
             disconnect();
@@ -141,32 +163,28 @@
         return true;
     }
 
-    private String executeCommand(String cmd) {
-        if (!writeCommand(cmd)) {
+    private Reply executeCommand(int cmd, String data) {
+        if (!writeCommand(cmd, data)) {
             /* If service died and restarted in the background
              * (unlikely but possible) we'll fail on the next
              * write (this one).  Try to reconnect and write
              * the command one more time before giving up.
              */
             Log.e(mTag, "write command failed? reconnect!");
-            if (!connect() || !writeCommand(cmd)) {
+            if (!connect() || !writeCommand(cmd, data)) {
                 return null;
             }
         }
-        if (readReply()) {
-            return new String(buf, 0, buflen);
-        } else {
-            return null;
-        }
+        return readReply();
     }
 
-    public synchronized String execute(String cmd) {
-      String result;
+    public synchronized Reply execute(int cmd, String data) {
+      Reply result;
       if (!connect()) {
           Log.e(mTag, "connection failed");
           return null;
       }
-      result = executeCommand(cmd);
+      result = executeCommand(cmd, data);
       disconnect();
       return result;
     }
diff --git a/keystore/jni/Android.mk b/keystore/jni/Android.mk
new file mode 100644
index 0000000..92c2d6d
--- /dev/null
+++ b/keystore/jni/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    cert.c certtool.c
+
+LOCAL_C_INCLUDES += \
+  $(JNI_H_INCLUDE) \
+  external/openssl/include
+
+LOCAL_SHARED_LIBRARIES := \
+  libcutils \
+  libnativehelper \
+  libutils \
+  libcrypto
+
+ifeq ($(TARGET_SIMULATOR),true)
+ifeq ($(TARGET_OS),linux)
+ifeq ($(TARGET_ARCH),x86)
+LOCAL_LDLIBS += -lpthread -ldl -lrt -lssl
+endif
+endif
+endif
+
+ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
+  LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
+endif
+
+LOCAL_MODULE:= libcerttool_jni
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c
new file mode 100644
index 0000000..07f0e86
--- /dev/null
+++ b/keystore/jni/cert.c
@@ -0,0 +1,249 @@
+/*
+**
+** Copyright 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.
+*/
+
+#define LOG_TAG "CertTool"
+
+#include <stdio.h>
+#include <openssl/engine.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rsa.h>
+#include <openssl/x509v3.h>
+#include <cutils/log.h>
+
+#include "cert.h"
+
+static PKEY_STORE pkey_store[KEYGEN_STORE_SIZE];
+static int store_index = 0;
+
+static char emsg[][30] = {
+    "",
+    STR(ERR_INVALID_KEY_LENGTH),
+    STR(ERR_CONSTRUCT_NEW_DATA),
+    STR(ERR_RSA_KEYGEN),
+    STR(ERR_X509_PROCESS),
+    STR(ERR_BIO_READ),
+};
+
+static void save_in_store(X509_REQ *req, EVP_PKEY *pkey)
+{
+    EVP_PKEY *newpkey = EVP_PKEY_new();
+    RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+    EVP_PKEY_set1_RSA(newpkey, rsa);
+    PKEY_STORE_free(pkey_store[store_index]);
+    pkey_store[store_index].key_len =
+    i2d_X509_PUBKEY(req->req_info->pubkey, &pkey_store[store_index].public_key);
+    pkey_store[store_index++].pkey = newpkey;
+    store_index %= KEYGEN_STORE_SIZE;
+    RSA_free(rsa);
+}
+
+static EVP_PKEY *get_pkey_from_store(X509 *cert)
+{
+    int i, key_len;
+    unsigned char *buf = NULL;
+    if ((key_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &buf)) == 0) {
+        return NULL;
+    }
+    for (i = 0 ; i < KEYGEN_STORE_SIZE ; ++i) {
+        if ((key_len == pkey_store[i].key_len) &&
+            memcmp(buf, pkey_store[i].public_key, key_len) == 0) {
+            break;
+        }
+    }
+    free(buf);
+    return (i == KEYGEN_STORE_SIZE) ? NULL : pkey_store[i].pkey;
+}
+
+int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX])
+{
+    int len, ret_code = 0;
+    BIGNUM *bn = NULL;
+    BIO *bio = NULL;
+    EVP_PKEY *pkey = NULL;
+    RSA *rsa = NULL;
+    X509_REQ *req = NULL;
+    X509_NAME *name = NULL;
+
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err;
+
+    if ((bits != KEYLENGTH_MEDIUM) && (bits != KEYLENGTH_MAXIMUM)) {
+        ret_code = ERR_INVALID_KEY_LENGTH;
+        goto err;
+    }
+
+    if (((pkey = EVP_PKEY_new()) == NULL) ||
+        ((req = X509_REQ_new()) == NULL) ||
+        ((rsa = RSA_new()) == NULL) || ((bn = BN_new()) == NULL)) {
+        ret_code = ERR_CONSTRUCT_NEW_DATA;
+        goto err;
+    }
+
+    if (!BN_set_word(bn, RSA_F4) ||
+        !RSA_generate_key_ex(rsa, bits, bn, NULL) ||
+        !EVP_PKEY_assign_RSA(pkey, rsa)) {
+        ret_code = ERR_RSA_KEYGEN;
+        goto err;
+    }
+
+    // rsa will be part of the req, it will be freed in X509_REQ_free(req)
+    rsa = NULL;
+
+    X509_REQ_set_pubkey(req, pkey);
+    name = X509_REQ_get_subject_name(req);
+
+    X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC,
+                               (const unsigned char *)"US", -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+                               (const unsigned char *) ANDROID_KEYSTORE,
+                               -1, -1, 0);
+    X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+                               (const unsigned char *)organizations, -1, -1, 0);
+
+    if (!X509_REQ_sign(req, pkey, EVP_md5()) ||
+        (PEM_write_bio_X509_REQ(bio, req) <= 0)) {
+        ret_code = ERR_X509_PROCESS;
+        goto err;
+    }
+    if ((len = BIO_read(bio, reply, REPLY_MAX - 1)) > 0) {
+      reply[len] = 0;
+      save_in_store(req, pkey);
+    } else {
+      ret_code = ERR_BIO_READ;
+    }
+
+err:
+    if (rsa) RSA_free(rsa);
+    if (bn) BN_free(bn);
+    if (req) X509_REQ_free(req);
+    if (pkey) EVP_PKEY_free(pkey);
+    if (bio) BIO_free(bio);
+    if ((ret_code > 0) && (ret_code < ERR_MAXIMUM)) LOGE(emsg[ret_code]);
+    return ret_code;
+}
+
+int is_pkcs12(const char *buf, int bufLen)
+{
+    int ret = 0;
+    BIO *bp = NULL;
+    PKCS12  *p12 = NULL;
+
+    if (!buf || bufLen < 1) goto err;
+
+    if (buf[0] != 48) goto err; // it is not DER.
+
+    if (!BIO_write(bp, buf, bufLen)) goto err;
+
+    if ((p12 = d2i_PKCS12_bio(bp, NULL)) != NULL) {
+        PKCS12_free(p12);
+        ret = 1;
+    }
+err:
+    if (bp) BIO_free(bp);
+    return ret;
+}
+
+X509* parse_cert(const char *buf, int bufLen)
+{
+    X509 *cert = NULL;
+    BIO *bp = NULL;
+
+    if(!buf || bufLen < 1)
+        return NULL;
+
+    bp = BIO_new(BIO_s_mem());
+    if (!bp) goto err;
+
+    if (!BIO_write(bp, buf, bufLen)) goto err;
+
+    cert = PEM_read_bio_X509(bp, NULL, NULL, NULL);
+    if (!cert) {
+        BIO_free(bp);
+        if((bp = BIO_new(BIO_s_mem())) == NULL) goto err;
+
+        if(!BIO_write(bp, (char *) buf, bufLen)) goto err;
+        cert = d2i_X509_bio(bp, NULL);
+   }
+
+err:
+    if (bp) BIO_free(bp);
+    return cert;
+}
+
+static int get_distinct_name(X509_NAME *dname, char *buf, int size)
+{
+   int i, len;
+   char *p, *name;
+
+   if (X509_NAME_oneline(dname, buf, size) == NULL) {
+      return -1;
+   }
+   name = strstr(buf, "/CN=");
+   p = name = name ? (name + 4) : buf;
+   while (*p != 0) {
+       if (*p == ' ') *p = '_';
+       if (*p == '/') {
+          *p = 0;
+          break;
+       }
+       ++p;
+   }
+   return 0;
+}
+
+int get_cert_name(X509 *cert, char *buf, int size)
+{
+   if (!cert) return -1;
+   return get_distinct_name(X509_get_subject_name(cert), buf, size);
+}
+
+int get_issuer_name(X509 *cert, char *buf, int size)
+{
+   if (!cert) return -1;
+   return get_distinct_name(X509_get_issuer_name(cert), buf, size);
+}
+
+int is_ca_cert(X509 *cert)
+{
+    int ret = 0;
+    BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)
+            X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL);
+    if (bs != NULL) ret = bs->ca;
+    if (bs) BASIC_CONSTRAINTS_free(bs);
+    return ret;
+}
+
+int get_private_key_pem(X509 *cert, char *buf, int size)
+{
+    int len = 0;
+    BIO *bio = NULL;
+    EVP_PKEY *pkey = get_pkey_from_store(cert);
+
+    if (pkey == NULL) return -1;
+
+    bio = BIO_new(BIO_s_mem());
+    if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err;
+    if (!PEM_write_bio_PrivateKey(bio, pkey, NULL,NULL,0,NULL, NULL)) {
+        goto err;
+    }
+    if ((len = BIO_read(bio, buf, size - 1)) > 0) {
+        buf[len] = 0;
+    }
+err:
+    if (bio) BIO_free(bio);
+    return (len == 0) ? -1 : 0;
+}
diff --git a/keystore/jni/cert.h b/keystore/jni/cert.h
new file mode 100644
index 0000000..a9807b1
--- /dev/null
+++ b/keystore/jni/cert.h
@@ -0,0 +1,59 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef __CERT_H__
+#define __CERT_H__
+
+#define ANDROID_KEYSTORE "Android Keystore"
+#define KEYGEN_STORE_SIZE     5
+#define KEYLENGTH_MEDIUM      1024
+#define KEYLENGTH_MAXIMUM     2048
+#define MAX_CERT_NAME_LEN     128
+#define MAX_PEM_LENGTH        4096
+#define REPLY_MAX             MAX_PEM_LENGTH
+
+
+#define STR(token) #token
+#define ERR_INVALID_KEY_LENGTH  1
+#define ERR_CONSTRUCT_NEW_DATA  2
+#define ERR_RSA_KEYGEN          3
+#define ERR_X509_PROCESS        4
+#define ERR_BIO_READ            5
+#define ERR_MAXIMUM             6
+
+typedef struct {
+    EVP_PKEY *pkey;
+    unsigned char *public_key;
+    int key_len;
+} PKEY_STORE;
+
+#define PKEY_STORE_free(x) { \
+    if(x.pkey) EVP_PKEY_free(x.pkey); \
+    if(x.public_key) free(x.public_key); \
+}
+
+#define nelem(x) (sizeof (x) / sizeof *(x))
+
+int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]);
+int is_pkcs12(const char *buf, int bufLen);
+X509*    parse_cert(const char *buf, int bufLen);
+int get_cert_name(X509 *cert, char *buf, int size);
+int get_issuer_name(X509 *cert, char *buf, int size);
+int is_ca_cert(X509 *cert);
+int get_private_key_pem(X509 *cert, char *buf, int size);
+
+#endif
diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c
new file mode 100644
index 0000000..c2a137e
--- /dev/null
+++ b/keystore/jni/certtool.c
@@ -0,0 +1,176 @@
+/*
+**
+** Copyright 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.
+*/
+#define LOG_TAG "CertTool"
+
+#include <string.h>
+#include <jni.h>
+#include <cutils/log.h>
+#include <openssl/x509v3.h>
+
+#include "cert.h"
+
+jstring
+android_security_CertTool_generateCertificateRequest(JNIEnv* env,
+                                                     jobject thiz,
+                                                     jint bits,
+                                                     jstring subject)
+
+{
+    char csr[REPLY_MAX];
+    if (gen_csr(bits, subject, csr) == 0) {
+        return (*env)->NewStringUTF(env, csr);
+    }
+    return NULL;
+}
+
+jboolean
+android_security_CertTool_isPkcs12Keystore(JNIEnv* env,
+                                           jobject thiz,
+                                           jbyteArray data)
+{
+    char buf[REPLY_MAX];
+    int len = (*env)->GetArrayLength(env, data);
+
+    if (len > REPLY_MAX) return 0;
+    (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+    return (jboolean) is_pkcs12(buf, len);
+}
+
+jint
+android_security_CertTool_generateX509Certificate(JNIEnv* env,
+                                                  jobject thiz,
+                                                  jbyteArray data)
+{
+    char buf[REPLY_MAX];
+    int len = (*env)->GetArrayLength(env, data);
+
+    if (len > REPLY_MAX) return 0;
+    (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+    return (jint) parse_cert(buf, len);
+}
+
+jboolean android_security_CertTool_isCaCertificate(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    return (handle == 0) ? (jboolean)0 : (jboolean) is_ca_cert((X509*)handle);
+}
+
+jstring android_security_CertTool_getIssuerDN(JNIEnv* env,
+                                              jobject thiz,
+                                              jint handle)
+{
+    char issuer[MAX_CERT_NAME_LEN];
+
+    if (handle == 0) return NULL;
+    if (get_issuer_name((X509*)handle, issuer, MAX_CERT_NAME_LEN)) return NULL;
+    return (*env)->NewStringUTF(env, issuer);
+}
+
+jstring android_security_CertTool_getCertificateDN(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    char name[MAX_CERT_NAME_LEN];
+    if (handle == 0) return NULL;
+    if (get_cert_name((X509*)handle, name, MAX_CERT_NAME_LEN)) return NULL;
+    return (*env)->NewStringUTF(env, name);
+}
+
+jstring android_security_CertTool_getPrivateKeyPEM(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    char pem[MAX_PEM_LENGTH];
+    if (handle == 0) return NULL;
+    if (get_private_key_pem((X509*)handle, pem, MAX_PEM_LENGTH)) return NULL;
+    return (*env)->NewStringUTF(env, pem);
+}
+
+void android_security_CertTool_freeX509Certificate(JNIEnv* env,
+                                                   jobject thiz,
+                                                   jint handle)
+{
+    if (handle != 0) X509_free((X509*)handle);
+}
+
+/*
+ * Table of methods associated with the CertTool class.
+ */
+static JNINativeMethod gCertToolMethods[] = {
+    /* name, signature, funcPtr */
+    {"generateCertificateRequest", "(ILjava/lang/String;)Ljava/lang/String;",
+        (void*)android_security_CertTool_generateCertificateRequest},
+    {"isPkcs12Keystore", "(B[)I",
+        (void*)android_security_CertTool_isPkcs12Keystore},
+    {"generateX509Certificate", "(B[)I",
+        (void*)android_security_CertTool_generateX509Certificate},
+    {"isCaCertificate", "(I)Z",
+        (void*)android_security_CertTool_isCaCertificate},
+    {"getIssuerDN", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getIssuerDN},
+    {"getCertificateDN", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getCertificateDN},
+    {"getPrivateKeyPEM", "(I)Ljava/lang/String;",
+        (void*)android_security_CertTool_getPrivateKeyPEM},
+    {"freeX509Certificate", "(I)V",
+        (void*)android_security_CertTool_freeX509Certificate},
+};
+
+/*
+ * Register several native methods for one class.
+ */
+static int registerNatives(JNIEnv* env, const char* className,
+                           JNINativeMethod* gMethods, int numMethods)
+{
+    jclass clazz;
+
+    clazz = (*env)->FindClass(env, className);
+    if (clazz == NULL) {
+        LOGE("Can not find class %s\n", className);
+        return JNI_FALSE;
+    }
+
+    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
+        LOGE("Can not RegisterNatives\n");
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        goto bail;
+    }
+
+    if (!registerNatives(env, "android/security/CertTool",
+                         gCertToolMethods, nelem(gCertToolMethods))) {
+        goto bail;
+    }
+
+    /* success -- return valid version number */
+    result = JNI_VERSION_1_4;
+
+bail:
+    return result;
+}
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 9783e54..a5b91f6 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -499,7 +499,8 @@
     }
 
 #ifdef WITH_A2DP
-    LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid());
+    LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(),
+            IPCThreadState::self()->getCallingPid());
     if (mode == AudioSystem::MODE_NORMAL && 
             (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) {
         AutoMutex lock(&mLock);
@@ -900,7 +901,7 @@
             }
             LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount);
         } else {
-            LOGE("mA2dpDisableCount is already zero");
+            LOGV("mA2dpDisableCount is already zero");
         }
     }
 }
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 734fba7..5f551df 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -173,6 +173,14 @@
     RS_TEX_ENV_MODE_DECAL
 };
 
+enum RsPrimitive {
+    RS_PRIMITIVE_POINT,
+    RS_PRIMITIVE_LINE,
+    RS_PRIMITIVE_LINE_STRIP,
+    RS_PRIMITIVE_TRIANGLE,
+    RS_PRIMITIVE_TRIANGLE_STRIP,
+    RS_PRIMITIVE_TRIANGLE_FAN
+};
 
 
 #include "rsgApiFuncDecl.h"
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
index a3b3d90..1687a31 100644
--- a/libs/rs/java/Film/res/raw/filmstrip.c
+++ b/libs/rs/java/Film/res/raw/filmstrip.c
@@ -31,6 +31,10 @@
     int trans;  // float
     int rot;   // float
     int x;
+    float focusPos;  // float
+    int focusID;
+    int lastFocusID;
+    int imgCount;
 
     float_2 = intToFloat(2);
     float_1 = intToFloat(1);
@@ -58,15 +62,17 @@
 
     //int imgId = 0;
 
-/*
-    contextBindProgramFragmentStore(env->fsImages);
-    contextBindProgramFragment(env->fpImages);
+    contextBindProgramFragmentStore(NAMED_PFImages);
+    contextBindProgramFragment(NAMED_PFSImages);
+
+    //focusPos = loadF(1, 2);
+    //focusID = 0;
+    //lastFocusID = loadI32(2, 0);
+    //imgCount = 13;
+
+    /*
     disable(GL_LIGHTING);
 
-    float focusPos = loadEnvF(1, 2);
-    int focusID = 0;
-    int lastFocusID = loadEnvI32(2, 0);
-    int imgCount = 13;
 
     if (trans > (-.3)) {
         focusID = -1.0 - focusPos;
diff --git a/libs/rs/java/Rollo/res/drawable/browser.png b/libs/rs/java/Rollo/res/drawable/browser.png
new file mode 100644
index 0000000..513f0be
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/browser.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/market.png b/libs/rs/java/Rollo/res/drawable/market.png
new file mode 100644
index 0000000..83b6910
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/market.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/photos.png b/libs/rs/java/Rollo/res/drawable/photos.png
new file mode 100644
index 0000000..1ed8f1e
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/photos.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/settings.png b/libs/rs/java/Rollo/res/drawable/settings.png
new file mode 100644
index 0000000..dd2cd95
--- /dev/null
+++ b/libs/rs/java/Rollo/res/drawable/settings.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c
index 56ee425..f181e49 100644
--- a/libs/rs/java/Rollo/res/raw/rollo.c
+++ b/libs/rs/java/Rollo/res/raw/rollo.c
@@ -1,14 +1,60 @@
 #pragma version(1)
 #pragma stateVertex(PV)
 #pragma stateFragment(PF)
-#pragma stateFragmentStore(PFSBackground)
+#pragma stateFragmentStore(PFS)
 
 int main(void* con, int ft, int launchID)
 {
+    int rowCount;
     int x;
+    int y;
+    int row;
+    int col;
+    int imageID;
+    int tx1;
+    int ty1;
+    int tz1;
+    int tx2;
+    int ty2;
+    int tz2;
+    int rot;
+    int rotStep;
+    int tmpSin;
+    int tmpCos;
+    int iconCount;
+    int pressure;
 
-    renderTriangleMesh(con, NAMED_MeshCard);
-    renderTriangleMesh(con, NAMED_MeshTab);
-    return 1;
+
+    rotStep = 20 * 0x10000;
+    pressure = loadI32(0, 2);
+
+    rowCount = 4;
+
+    iconCount = loadI32(0, 1);
+    rot = (-20 + loadI32(0, 0)) * 0x10000;
+    while (iconCount) {
+        tmpSin = sinx(rot);
+        tmpCos = cosx(rot);
+
+        tx1 = tmpSin * 8 - tmpCos;
+        tx2 = tx1 + tmpCos * 2;
+
+        tz1 = tmpCos * 8 + tmpSin + pressure;
+        tz2 = tz1 - tmpSin * 2;
+
+        for (y = 0; (y < rowCount) && iconCount; y++) {
+            ty1 = (y * 0x30000) - 0x48000;
+            ty2 = ty1 + 0x20000;
+            pfBindTexture(NAMED_PF, 0, loadI32(1, y));
+            drawQuad(tx1, ty1, tz1,
+                     tx2, ty1, tz2,
+                     tx2, ty2, tz2,
+                     tx1, ty2, tz1);
+            iconCount--;
+        }
+        rot = rot + rotStep;
+    }
+
+    return 0;
 }
 
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
index da0b146..91f25c2 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
@@ -48,6 +48,12 @@
         initRS();
     }
 
+    public void setPosition(float dx, float pressure) {
+        mAllocStateBuf[0] += (int)(dx);
+        mAllocStateBuf[2] = (int)(pressure * 0x40000);
+        mAllocState.data(mAllocStateBuf);
+    }
+
 
     private Resources mRes;
     private RenderScript mRS;
@@ -62,27 +68,21 @@
     private RenderScript.ProgramFragment mPFImages;
     private RenderScript.ProgramVertex mPV;
     private ProgramVertexAlloc mPVAlloc;
+    private RenderScript.Allocation[] mIcons;
+    private RenderScript.Allocation mIconPlate;
 
-    private RenderScript.Allocation mAllocEnv;
-    private RenderScript.Allocation mAllocPos;
+    private int[] mAllocStateBuf;
     private RenderScript.Allocation mAllocState;
-    //private RenderScript.Allocation mAllocPV;
-    private RenderScript.TriangleMesh mMeshCard;
-    private RenderScript.TriangleMesh mMeshTab;
 
-    private float[] mBufferPos;
-    //private float[] mBufferPV;
+    private int[] mAllocIconIDBuf;
+    private RenderScript.Allocation mAllocIconID;
 
     private void initNamed() {
-        mMeshTab = RolloMesh.createTab(mRS);
-        mMeshTab.setName("MeshTab");
-        mMeshCard = RolloMesh.createCard(mRS);
-        mMeshCard.setName("MeshCard");
-        Log.e("rs", "Done loading strips");
-
         mRS.samplerBegin();
         mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN,
-                       RenderScript.SamplerValue.LINEAR_MIP_LINEAR);
+                       RenderScript.SamplerValue.LINEAR);//_MIP_LINEAR);
+        mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG,
+                       RenderScript.SamplerValue.LINEAR);
         mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S,
                        RenderScript.SamplerValue.CLAMP);
         mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T,
@@ -92,27 +92,22 @@
 
         mRS.programFragmentBegin(null, null);
         mRS.programFragmentSetTexEnable(0, true);
+        //mRS.programFragmentSetTexEnable(1, true);
         //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE);
+        //mRS.programFragmentSetEnvMode(1, RS_TEX_ENV_MODE_MODULATE);
         mPFImages = mRS.programFragmentCreate();
         mPFImages.setName("PF");
         mPFImages.bindSampler(mSampler, 0);
+        mPFImages.bindSampler(mSampler, 1);
 
         mRS.programFragmentStoreBegin(null, null);
         mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
-        mRS.programFragmentStoreDitherEnable(true);
-        mPFSBackground = mRS.programFragmentStoreCreate();
-        mPFSBackground.setName("PFSBackground");
-
-        /*
-        mRS.programFragmentStoreBegin(null, null);
-        mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.EQUAL);
         mRS.programFragmentStoreDitherEnable(false);
         mRS.programFragmentStoreDepthMask(false);
         mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
                                           RenderScript.BlendDstFunc.ONE);
-        mPFSImages = mRS.programFragmentStoreCreate();
-        mPFSImages.setName("PFSImages");
-*/
+        mPFSBackground = mRS.programFragmentStoreCreate();
+        mPFSBackground.setName("PFS");
 
 
         mPVAlloc = new ProgramVertexAlloc(mRS);
@@ -122,22 +117,90 @@
         mPV.setName("PV");
         mPV.bindAllocation(0, mPVAlloc.mAlloc);
 
+
+
         mPVAlloc.setupProjectionNormalized(320, 480);
         //mPVAlloc.setupOrthoNormalized(320, 480);
         mRS.contextBindProgramVertex(mPV);
 
 
         Log.e("rs", "Done loading named");
+
+
+        {
+            mIcons = new RenderScript.Allocation[4];
+            mAllocIconIDBuf = new int[mIcons.length];
+            mAllocIconID = mRS.allocationCreatePredefSized(
+                RenderScript.ElementPredefined.USER_I32, mAllocIconIDBuf.length);
+
+            
+            BitmapDrawable bd;
+            Bitmap b;
+            
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.browser);
+            mIcons[0] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.market);
+            mIcons[1] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.photos);
+            mIcons[2] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            bd = (BitmapDrawable)mRes.getDrawable(R.drawable.settings);
+            mIcons[3] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+
+            for(int ct=0; ct < mIcons.length; ct++) {
+                mIcons[ct].uploadToTexture(0);
+                mAllocIconIDBuf[ct] = mIcons[ct].getID();
+            }
+            mAllocIconID.data(mAllocIconIDBuf);
+
+            RenderScript.Element e = mRS.elementGetPredefined(RenderScript.ElementPredefined.RGB_565);
+            mRS.typeBegin(e);
+            mRS.typeAdd(RenderScript.Dimension.X, 64);
+            mRS.typeAdd(RenderScript.Dimension.Y, 64);
+            RenderScript.Type t = mRS.typeCreate();
+            mIconPlate = mRS.allocationCreateTyped(t);
+            //t.destroy();
+            //e.destroy();
+
+            int tmp[] = new int[64 * 32];
+            for(int ct = 0; ct < (64*32); ct++) {
+                tmp[ct] = 7 | (13 << 5) | (7 << 11);
+                tmp[ct] = tmp[ct] | (tmp[ct] << 16);
+            }
+            for(int ct = 0; ct < 32; ct++) {
+                tmp[ct] = 0;
+                tmp[ct + (63*32)] = 0;
+            }
+            for(int ct = 0; ct < 64; ct++) {
+                tmp[ct * 32] = 0;
+                tmp[ct * 32 + 31] = 0;
+            }
+            mIconPlate.data(tmp);
+            Log.e("xx", "plate");
+            mIconPlate.uploadToTexture(0);
+            mIconPlate.setName("Plate");
+            mPFImages.bindTexture(mIconPlate, 0);
+        }
+
     }
 
 
+
     private void initRS() {
         mRS.scriptCBegin();
-        mRS.scriptCSetClearColor(0.0f, 0.7f, 0.0f, 1.0f);
+        mRS.scriptCSetClearColor(0.0f, 0.0f, 0.1f, 1.0f);
         mRS.scriptCSetScript(mRes, R.raw.rollo);
         mRS.scriptCSetRoot(true);
         mScript = mRS.scriptCCreate();
 
+        mAllocStateBuf = new int[] {0, 38, 0};
+        mAllocState = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length);
+        mScript.bindAllocation(mAllocState, 0);
+        mScript.bindAllocation(mAllocIconID, 1);
+        setPosition(0, 0);
 
         mRS.contextBindRootScript(mScript);
     }
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
index 9a30aed..27d2dd6 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
@@ -74,6 +74,11 @@
         if (act == ev.ACTION_UP) {
             ret = false;
         }
+        float x = ev.getX();
+        x = (x - 180) / 40;
+        //Log.e("rs", Float(x).toString());
+
+        mRender.setPosition(x, ev.getPressure());
         //mRender.newTouchPosition((int)ev.getX(), (int)ev.getY());
         return ret;
     }
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index bc14eac..c143307 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -164,6 +164,7 @@
     const Type * type = static_cast<const Type *>(vtype);
 
     Allocation * alloc = new Allocation(type);
+    alloc->incRef();
     return alloc;
 }
 
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index d98bfd9..f555090 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -21,6 +21,7 @@
 #include "rsMatrix.h"
 #include "rsAllocation.h"
 #include "rsTriangleMesh.h"
+#include "rsMesh.h"
 #include "rsDevice.h"
 #include "rsScriptC.h"
 #include "rsAllocation.h"
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
new file mode 100644
index 0000000..6eb95fc
--- /dev/null
+++ b/libs/rs/rsMesh.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+Mesh::Mesh()
+{
+    mSources = NULL;
+    mPrimitives = NULL;
+    mPrimitiveCount = 0;
+}
+
+Mesh::~Mesh()
+{
+}
+
+
+
+MeshContext::MeshContext()
+{
+}
+
+MeshContext::~MeshContext()
+{
+}
+
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
new file mode 100644
index 0000000..c6d3bc9
--- /dev/null
+++ b/libs/rs/rsMesh.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_RS_MESH_H
+#define ANDROID_RS_MESH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Mesh : public ObjectBase
+{
+public:
+    Mesh();
+    ~Mesh();
+
+    struct VertexSource_t
+    {
+        const Element * mVertexElement;
+        void * mVertexData;
+        size_t mVertexDataSize;
+
+        size_t mOffsetCoord;
+        size_t mOffsetTex;
+        size_t mOffsetNorm;
+    
+        size_t mSizeCoord;
+        size_t mSizeTex;
+        size_t mSizeNorm;
+
+        uint32_t mBufferObject;
+    };
+
+    struct Primitive_t
+    {
+        RsPrimitive mType;
+        const Element * mIndexElement;
+        void * mVertexData;
+        size_t mIndexDataSize;
+
+        uint32_t mBufferObject;
+    };
+
+    VertexSource_t * mSources;
+    Primitive_t * mPrimitives;
+    uint32_t mPrimitiveCount;
+
+    void analyzeElement();
+protected:
+};
+
+class MeshContext
+{
+public:
+    MeshContext();
+    ~MeshContext();
+
+};
+
+
+}
+}
+#endif //ANDROID_RS_TRIANGLE_MESH_H
+
+
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index f2e7095..628f93e 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -32,6 +32,7 @@
         mTextureDimensions[ct] = 2;
     }
     mTextureEnableMask = 0;
+    mEnvModes[1] = RS_TEX_ENV_MODE_DECAL;
 }
 
 ProgramFragment::~ProgramFragment()
@@ -42,10 +43,7 @@
 {
     for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
         glActiveTexture(GL_TEXTURE0 + ct);
-        if (!(mTextureEnableMask & (1 << ct)) ||
-            //!mSamplers[ct].get() ||
-            !mTextures[ct].get()) {
-
+        if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) {
             glDisable(GL_TEXTURE_2D);
             continue;
         }
@@ -55,13 +53,13 @@
 
         switch(mEnvModes[ct]) {
         case RS_TEX_ENV_MODE_REPLACE:
-            glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
             break;
         case RS_TEX_ENV_MODE_MODULATE:
-            glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
             break;
         case RS_TEX_ENV_MODE_DECAL:
-            glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_DECAL);
+            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
             break;
         }
 
@@ -70,10 +68,29 @@
         } else {
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
         }
+
+        // Gross hack.
+        if (ct == 2) {
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+        }
     }
+
+
     glActiveTexture(GL_TEXTURE0);
 }
 
@@ -85,6 +102,7 @@
         return;
     }
 
+    //LOGE("bindtex %i %p", slot, a);
     mTextures[slot].set(a);
 }
 
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 522ed00..0ec6baf 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -307,6 +307,67 @@
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 }
 
+extern "C" void drawQuad(int32_t x1, int32_t y1, int32_t z1,
+                         int32_t x2, int32_t y2, int32_t z2,
+                         int32_t x3, int32_t y3, int32_t z3,
+                         int32_t x4, int32_t y4, int32_t z4)
+{
+    GET_TLS();
+    //x1 = (x1 << 16);
+    //x2 = (x2 << 16);
+    //y1 = (y1 << 16);
+    //y2 = (y2 << 16);
+
+    //LOGE("Quad");
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x1, y1, z1);
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x2, y2, z2);
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x3, y3, z3);
+    //LOGE("0x%08x, 0x%08x, 0x%08x", x4, y4, z4);
+
+    int32_t vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
+    static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0x10000, 0x10000,0};
+
+
+    rsc->setupCheck();
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glVertexPointer(3, GL_FIXED, 0, vtx);
+
+    glClientActiveTexture(GL_TEXTURE0);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glTexCoordPointer(2, GL_FIXED, 0, tex);
+    glClientActiveTexture(GL_TEXTURE1);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    glTexCoordPointer(2, GL_FIXED, 0, tex);
+    glClientActiveTexture(GL_TEXTURE0);
+
+    glDisableClientState(GL_NORMAL_ARRAY);
+    glDisableClientState(GL_COLOR_ARRAY);
+
+    //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+}
+
+extern "C" int32_t sinx(int32_t angle)
+{
+    float a = ((float)angle) / 0x10000;
+    a *= 3.14f / 180.f;
+    float s = (float)sin(a);
+    return int32_t(s * 0x10000);
+}
+
+extern "C" int32_t cosx(int32_t angle)
+{
+    float a = ((float)angle) / 0x10000;
+    a *= 3.14f / 180.f;
+    float s = (float)cos(a);
+    return int32_t(s * 0x10000);
+}
+
 extern "C" void pfBindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
 {
     GET_TLS();
diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp
index 68c0a5e..3b1b152 100644
--- a/libs/surfaceflinger/VRamHeap.cpp
+++ b/libs/surfaceflinger/VRamHeap.cpp
@@ -55,7 +55,7 @@
  * (PMEM is used for 2D acceleration)
  * 8 MB of address space per client should be enough.
  */
-static const int PMEM_SIZE = int(8 * 1024 * 1024);
+static const int PMEM_SIZE = int(16 * 1024 * 1024);
 
 int SurfaceHeapManager::global_pmem_heap = 0;
 
@@ -79,7 +79,11 @@
         mPMemHeap = new PMemHeap(device, PMEM_SIZE);
         if (mPMemHeap->base() == MAP_FAILED) {
             mPMemHeap.clear();
-            global_pmem_heap = 0;
+            mPMemHeap = new PMemHeap(device, PMEM_SIZE/2);
+            if (mPMemHeap->base() == MAP_FAILED) {
+                mPMemHeap.clear();
+                global_pmem_heap = 0;
+            }
         }
     }
 }
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 7a33220..4a5063a 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -3845,7 +3845,7 @@
                             & Res_value::COMPLEX_RADIX_MASK];
     printf("%f", value);
     
-    if (isFraction) {
+    if (!isFraction) {
         switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
             case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
             case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break;
@@ -3864,6 +3864,49 @@
     }
 }
 
+void ResTable::print_value(const Package* pkg, const Res_value& value) const
+{
+    if (value.dataType == Res_value::TYPE_NULL) {
+        printf("(null)\n");
+    } else if (value.dataType == Res_value::TYPE_REFERENCE) {
+        printf("(reference) 0x%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) {
+        printf("(attribute) 0x%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_STRING) {
+        size_t len;
+        const char16_t* str = pkg->header->values.stringAt(
+                value.data, &len);
+        if (str == NULL) {
+            printf("(string) null\n");
+        } else {
+            printf("(string) \"%s\"\n",
+                    String8(str, len).string());
+        } 
+    } else if (value.dataType == Res_value::TYPE_FLOAT) {
+        printf("(float) %g\n", *(const float*)&value.data);
+    } else if (value.dataType == Res_value::TYPE_DIMENSION) {
+        printf("(dimension) ");
+        print_complex(value.data, false);
+        printf("\n");
+    } else if (value.dataType == Res_value::TYPE_FRACTION) {
+        printf("(fraction) ");
+        print_complex(value.data, true);
+        printf("\n");
+    } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT
+            || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) {
+        printf("(color) #%08x\n", value.data);
+    } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) {
+        printf("(boolean) %s\n", value.data ? "true" : "false");
+    } else if (value.dataType >= Res_value::TYPE_FIRST_INT
+            || value.dataType <= Res_value::TYPE_LAST_INT) {
+        printf("(int) 0x%08x or %d\n", value.data, value.data);
+    } else {
+        printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n",
+               (int)value.dataType, (int)value.data,
+               (int)value.size, (int)value.res0);
+    }
+}
+
 void ResTable::print(bool inclValues) const
 {
     if (mError != 0) {
@@ -3985,27 +4028,31 @@
                             continue;
                         }
                         
-                        const Res_value* value = NULL;
+                        uint16_t esize = dtohs(ent->size);
+                        if ((esize&0x3) != 0) {
+                            printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
+                            continue;
+                        }
+                        if ((thisOffset+esize) > typeSize) {
+                            printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
+                                   (void*)entriesStart, (void*)thisOffset,
+                                   (void*)esize, (void*)typeSize);
+                            continue;
+                        }
+                            
+                        const Res_value* valuePtr = NULL;
+                        const ResTable_map_entry* bagPtr = NULL;
+                        Res_value value;
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
                             printf("<bag>");
+                            bagPtr = (const ResTable_map_entry*)ent;
                         } else {
-                            uint16_t esize = dtohs(ent->size);
-                            if ((esize&0x3) != 0) {
-                                printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
-                                continue;
-                            }
-                            if ((thisOffset+esize) > typeSize) {
-                                printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n",
-                                       (void*)entriesStart, (void*)thisOffset,
-                                       (void*)esize, (void*)typeSize);
-                                continue;
-                            }
-                            
-                            value = (const Res_value*)
+                            valuePtr = (const Res_value*)
                                 (((const uint8_t*)ent) + esize);
+                            value.copyFrom_dtoh(*valuePtr);
                             printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
-                                   (int)value->dataType, (int)dtohl(value->data),
-                                   (int)dtohs(value->size), (int)value->res0);
+                                   (int)value.dataType, (int)value.data,
+                                   (int)value.size, (int)value.res0);
                         }
                         
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
@@ -4014,44 +4061,23 @@
                         printf("\n");
                         
                         if (inclValues) {
-                            if (value != NULL) {
+                            if (valuePtr != NULL) {
                                 printf("          ");
-                                if (value->dataType == Res_value::TYPE_NULL) {
-                                    printf("(null)\n");
-                                } else if (value->dataType == Res_value::TYPE_REFERENCE) {
-                                    printf("(reference) 0x%08x\n", value->data);
-                                } else if (value->dataType == Res_value::TYPE_ATTRIBUTE) {
-                                    printf("(attribute) 0x%08x\n", value->data);
-                                } else if (value->dataType == Res_value::TYPE_STRING) {
-                                    size_t len;
-                                    const char16_t* str = pkg->header->values.stringAt(
-                                            value->data, &len);
-                                    if (str == NULL) {
-                                        printf("(string) null\n");
-                                    } else {
-                                        printf("(string) \"%s\"\n",
-                                                String8(str, len).string());
-                                    } 
-                                } else if (value->dataType == Res_value::TYPE_FLOAT) {
-                                    printf("(float) %g\n", *(const float*)&value->data);
-                                } else if (value->dataType == Res_value::TYPE_DIMENSION) {
-                                    printf("(dimension) ");
-                                    print_complex(value->data, false);
-                                    printf("\n");
-                                } else if (value->dataType == Res_value::TYPE_FRACTION) {
-                                    printf("(fraction) ");
-                                    print_complex(value->data, true);
-                                    printf("\n");
-                                } else if (value->dataType >= Res_value::TYPE_FIRST_COLOR_INT
-                                        || value->dataType <= Res_value::TYPE_LAST_COLOR_INT) {
-                                    printf("(color) #%08x\n", value->data);
-                                } else if (value->dataType == Res_value::TYPE_INT_BOOLEAN) {
-                                    printf("(boolean) %s\n", value->data ? "true" : "false");
-                                } else if (value->dataType >= Res_value::TYPE_FIRST_INT
-                                        || value->dataType <= Res_value::TYPE_LAST_INT) {
-                                    printf("(int) 0x%08x or %d\n", value->data, value->data);
-                                } else {
-                                    printf("(unknown type)\n");
+                                print_value(pkg, value);
+                            } else if (bagPtr != NULL) {
+                                const int N = dtohl(bagPtr->count);
+                                const ResTable_map* mapPtr = (const ResTable_map*)
+                                        (((const uint8_t*)ent) + esize);
+                                printf("          Parent=0x%08x, Count=%d\n",
+                                    dtohl(bagPtr->parent.ident), N);
+                                for (int i=0; i<N; i++) {
+                                    printf("          #%i (Key=0x%08x): ",
+                                        i, dtohl(mapPtr->name.ident));
+                                    value.copyFrom_dtoh(mapPtr->value);
+                                    print_value(pkg, value);
+                                    const size_t size = dtohs(mapPtr->value.size);
+                                    mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
+                                            + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
                                 }
                             }
                         }
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 883e5f5..3f0c234 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -616,6 +616,9 @@
             synchronized(mListeners) {
                 mListeners.remove(this);
             }
+            if (mListener != null) {
+                mListener.asBinder().unlinkToDeath(this, 0);
+            }
         }
     }
 
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index bd7088c..4ae424a 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -53,6 +53,12 @@
         }
     }
 
+    public void unlinkProvider() {
+        if (mProvider != null) {
+            mProvider.asBinder().unlinkToDeath(this, 0);
+        }
+    }
+
     public String getName() {
         return mName;
     }
@@ -255,5 +261,6 @@
     public void binderDied() {
         Log.w(TAG, "Location Provider " + mName + " died");
         mDead = true;
+        mProvider.asBinder().unlinkToDeath(this, 0);
     }
 }
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 87f4c40..af02741 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -42,4 +42,7 @@
     <bool name="def_usb_mass_storage_enabled">true</bool>
     <bool name="def_wifi_on">false</bool>
     <bool name="def_networks_available_notification_on">true</bool>
+    
+    <bool name="def_backup_enabled">false</bool>
+    <string name="def_backup_transport"></string>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 602f3e1..2b888e4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -64,7 +64,7 @@
 
     private static final String TAG = "SettingsProvider";
     private static final String DATABASE_NAME = "settings.db";
-    private static final int DATABASE_VERSION = 35;
+    private static final int DATABASE_VERSION = 36;
 
     private Context mContext;
 
@@ -401,6 +401,20 @@
             upgradeVersion = 35;
         }
 
+        if (upgradeVersion == 35) {
+            db.beginTransaction();
+            try {
+                SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadSecure35Settings(stmt);
+                stmt.close();
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+            upgradeVersion = 36;
+        }
+        
         if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -708,9 +722,19 @@
         loadSetting(stmt, Settings.Secure.ALLOW_MOCK_LOCATION,
                 "1".equals(SystemProperties.get("ro.allow.mock.location")) ? 1 : 0);
 
+        loadSecure35Settings(stmt);
+        
         stmt.close();
     }
 
+    private void loadSecure35Settings(SQLiteStatement stmt) {
+        loadBooleanSetting(stmt, Settings.Secure.BACKUP_ENABLED,
+                R.bool.def_backup_enabled);
+        
+        loadStringSetting(stmt, Settings.Secure.BACKUP_TRANSPORT,
+                R.string.def_backup_transport);
+    }
+    
     private void loadSetting(SQLiteStatement stmt, String key, Object value) {
         stmt.bindString(1, key);
         stmt.bindString(2, value.toString());
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index b1e6425..b5f5b37 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -51,10 +51,13 @@
         public static final int IPA = 1;
         public static final int EARCON = 2;
         public static final int SILENCE = 3;
+        public static final int TEXT_TO_FILE = 5;
+        public static final int IPA_TO_FILE = 6;
         public String mText = null;
         public ArrayList<String> mParams = null;
         public int mType = TEXT;
         public long mDuration = 0;
+        public String mFilename = null;
 
         public SpeechItem(String text, ArrayList<String> params, int itemType) {
             mText = text;
@@ -65,6 +68,14 @@
         public SpeechItem(long silenceTime) {
             mDuration = silenceTime;
         }
+
+        public SpeechItem(String text, ArrayList<String> params, int itemType, String filename) {
+            mText = text;
+            mParams = params;
+            mType = itemType;
+            mFilename = filename;
+        }
+
     }
 
     /**
@@ -464,6 +475,60 @@
         synth.start();
     }
 
+    private void synthToFileInternalOnly(final String text,
+            final ArrayList<String> params, final String filename) {
+        class SynthThread implements Runnable {
+            public void run() {
+                Log.i("TTS", "Synthesizing to " + filename);
+                boolean synthAvailable = false;
+                try {
+                    synthAvailable = synthesizerLock.tryLock();
+                    if (!synthAvailable) {
+                        Thread.sleep(100);
+                        Thread synth = (new Thread(new SynthThread()));
+                        synth.setPriority(Thread.MIN_PRIORITY);
+                        synth.start();
+                        return;
+                    }
+                    if (params != null){
+                        String language = "";
+                        String country = "";
+                        String variant = "";
+                        for (int i = 0; i < params.size() - 1; i = i + 2){
+                            String param = params.get(i);
+                            if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
+                                setSpeechRate(Integer.parseInt(params.get(i+1)));
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
+                                language = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
+                                country = params.get(i+1);
+                            } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
+                                variant = params.get(i+1);
+                            }
+                        }
+                        if (language.length() > 0){
+                            setLanguage(language, country, variant);
+                        }
+                    }
+                    nativeSynth.synthesizeToFile(text, filename);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    // This check is needed because finally will always run;
+                    // even if the
+                    // method returns somewhere in the try block.
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
+                    processSpeechQueue();
+                }
+            }
+        }
+        Thread synth = (new Thread(new SynthThread()));
+        synth.setPriority(Thread.MIN_PRIORITY);
+        synth.start();
+    }
+
     private SoundResource getSoundResource(SpeechItem speechItem) {
         SoundResource sr = null;
         String text = speechItem.mText;
@@ -549,6 +614,9 @@
                     currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem);
                     speakInternalOnly(currentSpeechItem.mText,
                             currentSpeechItem.mParams);
+                } else if (currentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
+                    synthToFileInternalOnly(currentSpeechItem.mText,
+                            currentSpeechItem.mParams, currentSpeechItem.mFilename);
                 } else if (currentSpeechItem.mType == SpeechItem.IPA) {
                     // TODO Implement IPA support
                 } else {
@@ -629,34 +697,20 @@
      * @return A boolean that indicates if the synthesis succeeded
      */
     private boolean synthesizeToFile(String text, ArrayList<String> params,
-            String filename, boolean calledFromApi) {
-        // Only stop everything if this is a call made by an outside app trying
-        // to
-        // use the API. Do NOT stop if this is a call from within the service as
-        // clearing the speech queue here would be a mistake.
-        if (calledFromApi) {
-            stop();
+            String filename) {
+        // Don't allow a filename that is too long
+        if (filename.length() > MAX_FILENAME_LENGTH) {
+            return false;
         }
-        Log.i("TTS", "Synthesizing to " + filename);
-        boolean synthAvailable = false;
-        try {
-            synthAvailable = synthesizerLock.tryLock();
-            if (!synthAvailable) {
-                return false;
-            }
-            // Don't allow a filename that is too long
-            if (filename.length() > MAX_FILENAME_LENGTH) {
-                return false;
-            }
-            nativeSynth.synthesizeToFile(text, filename);
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (synthAvailable) {
-                synthesizerLock.unlock();
-            }
+        // Don't allow anything longer than the max text length; since this
+        // is synthing to a file, don't even bother splitting it.
+        if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+            return false;
         }
-        Log.i("TTS", "Completed synthesis for " + filename);
+        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT_TO_FILE, filename));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
         return true;
     }
 
@@ -957,7 +1011,7 @@
             if (params != null) {
                 speakingParams = new ArrayList<String>(Arrays.asList(params));
             }
-            return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+            return mSelf.synthesizeToFile(text, speakingParams, filename);
         }
 
         /**
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index 2ad218f..7dd9d9e 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -78,20 +78,10 @@
 
     /**
      * Sends a command with arguments to the service through the control socket.
-     * Each argument is sent as a C-style zero-terminated string.
      */
     public void sendCommand(String ...args) throws IOException {
         OutputStream out = getControlSocketOutput();
         for (String arg : args) outputString(out, arg);
-        checkSocketResult();
-    }
-
-    /**
-     * Sends a command with arguments to the service through the control socket.
-     */
-    public void sendCommand2(String ...args) throws IOException {
-        OutputStream out = getControlSocketOutput();
-        for (String arg : args) outputString2(out, arg);
         out.write(END_OF_ARGUMENTS);
         out.flush();
         checkSocketResult();
@@ -128,8 +118,9 @@
 
                         if (data == 0) {
                             // re-establish the connection:
-                            // synchronized here so that checkSocketResult() returns
-                            // when new mKeepaliveSocket is available for next cmd
+                            // synchronized here so that checkSocketResult()
+                            // returns when new mKeepaliveSocket is available for
+                            // next cmd
                             synchronized (this) {
                                 setResultAndCloseControlSocket((byte) data);
                                 s = mKeepaliveSocket = createServiceSocket();
@@ -244,12 +235,6 @@
     }
 
     private void outputString(OutputStream out, String s) throws IOException {
-        out.write(s.getBytes());
-        out.write(0);
-        out.flush();
-    }
-
-    private void outputString2(OutputStream out, String s) throws IOException {
         byte[] bytes = s.getBytes();
         out.write(bytes.length);
         out.write(bytes);
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
new file mode 100644
index 0000000..6abf81c
--- /dev/null
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java
@@ -0,0 +1,49 @@
+/*
+ * 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.server.vpn;
+
+import android.net.vpn.L2tpIpsecPskProfile;
+
+import java.io.IOException;
+
+/**
+ * The service that manages the preshared key based L2TP-over-IPSec VPN
+ * connection.
+ */
+class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> {
+    private static final String IPSEC_DAEMON = "racoon";
+
+    @Override
+    protected void connect(String serverIp, String username, String password)
+            throws IOException {
+        String hostIp = getHostIp();
+        L2tpIpsecPskProfile p = getProfile();
+
+        // IPSEC
+        AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
+        ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+                p.getPresharedKey());
+
+        sleep(2000); // 2 seconds
+
+        // L2TP
+        MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp,
+                L2tpService.L2TP_PORT,
+                (p.isSecretEnabled() ? p.getSecretString() : null),
+                username, password);
+    }
+}
diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
index 877fa6b..bd14110 100644
--- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java
@@ -22,7 +22,7 @@
 import java.io.IOException;
 
 /**
- * The service that manages the L2TP-over-IPSec VPN connection.
+ * The service that manages the certificate based L2TP-over-IPSec VPN connection.
  */
 class L2tpIpsecService extends VpnService<L2tpIpsecProfile> {
     private static final String IPSEC_DAEMON = "racoon";
@@ -34,11 +34,10 @@
 
         // IPSEC
         AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON);
-        ipsecService.sendCommand(
-                String.format("SETKEY %s %s", hostIp, serverIp));
-        ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s",
-                serverIp, getCaCertPath(), getUserCertPath(),
-                getUserkeyPath()));
+        ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT,
+                getUserkeyPath(), getUserCertPath(), getCaCertPath());
+
+        sleep(2000); // 2 seconds
 
         // L2TP
         L2tpIpsecProfile p = getProfile();
diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
index 6160900..16d253a 100644
--- a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
+++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java
@@ -38,7 +38,7 @@
         addPppArguments(vpnService, args, serverIp, username, password);
 
         AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE);
-        mtpd.sendCommand2(args.toArray(new String[args.size()]));
+        mtpd.sendCommand(args.toArray(new String[args.size()]));
     }
 
     private static void addPppArguments(VpnService<?> vpnService,
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index 44127ff..6e5d46b 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -24,14 +24,12 @@
 import android.net.vpn.VpnManager;
 import android.net.vpn.VpnProfile;
 import android.net.vpn.VpnState;
-import android.os.FileObserver;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 
-import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
-import java.net.InetSocketAddress;
 import java.net.NetworkInterface;
 import java.net.Socket;
 import java.util.ArrayList;
@@ -43,21 +41,18 @@
  */
 abstract class VpnService<E extends VpnProfile> {
     private static final int NOTIFICATION_ID = 1;
-    private static final String PROFILES_ROOT = VpnManager.PROFILES_PATH + "/";
-    public static final String DEFAULT_CONFIG_PATH = "/etc";
 
-    private static final int DNS_TIMEOUT = 3000; // ms
     private static final String DNS1 = "net.dns1";
     private static final String DNS2 = "net.dns2";
+    private static final String VPN_DNS1 = "vpn.dns1";
+    private static final String VPN_DNS2 = "vpn.dns2";
+    private static final String VPN_UP = "vpn.up";
+    private static final String VPN_IS_UP = "1";
+    private static final String VPN_IS_DOWN = "0";
+
     private static final String REMOTE_IP = "net.ipremote";
     private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
-    private static final String SERVER_IP = "net.vpn.server_ip";
 
-    private static final int VPN_TIMEOUT = 30000; // milliseconds
-    private static final int ONE_SECOND = 1000; // milliseconds
-    private static final int FIVE_SECOND = 5000; // milliseconds
-
-    private static final String LOGWRAPPER = "/system/bin/logwrapper";
     private final String TAG = VpnService.class.getSimpleName();
 
     E mProfile;
@@ -76,13 +71,6 @@
 
     private long mStartTime; // VPN connection start time
 
-    // monitors if the VPN connection is sucessfully established
-    private FileMonitor mConnectMonitor;
-
-    // watch dog timer; fired up if the connection cannot be established within
-    // VPN_TIMEOUT
-    private Object mWatchdog;
-
     // for helping managing multiple Android services
     private ServiceHelper mServiceHelper = new ServiceHelper();
 
@@ -110,19 +98,6 @@
         return mServiceHelper.startService(serviceName);
     }
 
-    protected String getPppOptionFilePath() throws IOException {
-        String subpath = getProfileSubpath("/ppp/peers");
-        String[] kids = new File(subpath).list();
-        if ((kids == null) || (kids.length == 0)) {
-            throw new IOException("no option file found in " + subpath);
-        }
-        if (kids.length > 1) {
-            Log.w(TAG, "more than one option file found in " + subpath
-                    + ", arbitrarily choose " + kids[0]);
-        }
-        return subpath + "/" + kids[0];
-    }
-
     /**
      * Returns the VPN profile associated with the connection.
      */
@@ -131,23 +106,6 @@
     }
 
     /**
-     * Returns the profile path where configuration files reside.
-     */
-    protected String getProfilePath() throws IOException {
-        String path = PROFILES_ROOT + mProfile.getId();
-        File dir = new File(path);
-        if (!dir.exists()) throw new IOException("Profile dir does not exist");
-        return path;
-    }
-
-    /**
-     * Returns the path where default configuration files reside.
-     */
-    protected String getDefaultConfigPath() throws IOException {
-        return DEFAULT_CONFIG_PATH;
-    }
-
-    /**
      * Returns the host IP for establishing the VPN connection.
      */
     protected String getHostIp() throws IOException {
@@ -178,14 +136,6 @@
     }
 
     /**
-     * Returns the path of the script file that is executed when the VPN
-     * connection is established.
-     */
-    protected String getConnectMonitorFile() {
-        return "/etc/ppp/ip-up-vpn";
-    }
-
-    /**
      * Sets the system property. The method is blocked until the value is
      * settled in.
      * @param name the name of the property
@@ -222,10 +172,10 @@
         broadcastConnectivity(VpnState.CONNECTING);
 
         String serverIp = getIp(getProfile().getServerName());
-        setSystemProperty(SERVER_IP, serverIp);
-        onBeforeConnect();
 
+        onBeforeConnect();
         connect(serverIp, username, password);
+        waitUntilConnectedOrTimedout();
     }
 
     synchronized void onDisconnect(boolean cleanUpServices) {
@@ -259,39 +209,36 @@
         }
     }
 
-    private void createConnectMonitor() {
-        mConnectMonitor = new FileMonitor(getConnectMonitorFile(),
-                new Runnable() {
-                    public void run() {
-                        onConnectMonitorTriggered();
-                    }
-                });
-    }
-
     private void onBeforeConnect() {
         mNotification.disableNotification();
 
-        createConnectMonitor();
-        mConnectMonitor.startWatching();
-        saveOriginalDnsProperties();
-
-        mWatchdog = startTimer(VPN_TIMEOUT, new Runnable() {
-            public void run() {
-                synchronized (VpnService.this) {
-                    if (mState == VpnState.CONNECTING) {
-                        Log.d(TAG, "       watchdog timer is fired !!");
-                        onError();
-                    }
-                }
-            }
-        });
+        SystemProperties.set(VPN_DNS1, "-");
+        SystemProperties.set(VPN_DNS2, "-");
+        SystemProperties.set(VPN_UP, VPN_IS_DOWN);
+        Log.d(TAG, "       VPN UP: " + SystemProperties.get(VPN_UP));
     }
 
-    private synchronized void onConnectMonitorTriggered() {
-        Log.d(TAG, "onConnectMonitorTriggered()");
+    private void waitUntilConnectedOrTimedout() {
+        sleep(2000); // 2 seconds
+        for (int i = 0; i < 60; i++) {
+            if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) {
+                onConnected();
+                return;
+            }
+            sleep(500); // 0.5 second
+        }
 
-        stopTimer(mWatchdog);
-        mConnectMonitor.stopWatching();
+        synchronized (this) {
+            if (mState == VpnState.CONNECTING) {
+                Log.d(TAG, "       connecting timed out !!");
+                onError();
+            }
+        }
+    }
+
+    private synchronized void onConnected() {
+        Log.d(TAG, "onConnected()");
+
         saveVpnDnsProperties();
         saveAndSetDomainSuffices();
         startConnectivityMonitor();
@@ -310,8 +257,6 @@
 
         restoreOriginalDnsProperties();
         restoreOriginalDomainSuffices();
-        if (mConnectMonitor != null) mConnectMonitor.stopWatching();
-        if (mWatchdog != null) stopTimer(mWatchdog);
         mState = VpnState.IDLE;
         broadcastConnectivity(VpnState.IDLE);
 
@@ -345,13 +290,6 @@
         }
     }
 
-    private void saveOriginalDnsProperties() {
-        mOriginalDns1 = SystemProperties.get(DNS1);
-        mOriginalDns2 = SystemProperties.get(DNS2);
-        Log.d(TAG, String.format("save original dns prop: %s, %s",
-                mOriginalDns1, mOriginalDns2));
-    }
-
     private void restoreOriginalDnsProperties() {
         // restore only if they are not overridden
         if (mVpnDns1.equals(SystemProperties.get(DNS1))) {
@@ -365,15 +303,21 @@
     }
 
     private void saveVpnDnsProperties() {
-        mVpnDns1 = mVpnDns2 = "";
+        mOriginalDns1 = mOriginalDns2 = "";
         for (int i = 0; i < 10; i++) {
-            mVpnDns1 = SystemProperties.get(DNS1);
-            mVpnDns2 = SystemProperties.get(DNS2);
-            if (mVpnDns1.equals(mOriginalDns1)) {
+            mVpnDns1 = SystemProperties.get(VPN_DNS1);
+            mVpnDns2 = SystemProperties.get(VPN_DNS2);
+            if (mOriginalDns1.equals(mVpnDns1)) {
                 Log.d(TAG, "wait for vpn dns to settle in..." + i);
                 sleep(500);
             } else {
-                Log.d(TAG, String.format("save vpn dns prop: %s, %s",
+                mOriginalDns1 = SystemProperties.get(DNS1);
+                mOriginalDns2 = SystemProperties.get(DNS2);
+                SystemProperties.set(DNS1, mVpnDns1);
+                SystemProperties.set(DNS2, mVpnDns2);
+                Log.d(TAG, String.format("save original dns prop: %s, %s",
+                        mOriginalDns1, mOriginalDns2));
+                Log.d(TAG, String.format("set vpn dns prop: %s, %s",
                         mVpnDns1, mVpnDns2));
                 return;
             }
@@ -381,23 +325,11 @@
         Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??");
     }
 
-    private void restoreVpnDnsProperties() {
-        if (isNullOrEmpty(mVpnDns1) && isNullOrEmpty(mVpnDns2)) {
-            return;
-        }
-        Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
-                SystemProperties.get(DNS1), mVpnDns1));
-        Log.d(TAG, String.format("restore vpn dns prop: %s --> %s",
-                SystemProperties.get(DNS2), mVpnDns2));
-        SystemProperties.set(DNS1, mVpnDns1);
-        SystemProperties.set(DNS2, mVpnDns2);
-    }
-
     private void saveAndSetDomainSuffices() {
         mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES);
         Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices);
         String list = mProfile.getDomainSuffices();
-        if (!isNullOrEmpty(list)) {
+        if (!TextUtils.isEmpty(list)) {
             SystemProperties.set(DNS_DOMAIN_SUFFICES, list);
         }
     }
@@ -423,7 +355,7 @@
                             if (mState != VpnState.CONNECTED) break;
                             mNotification.update();
                             checkConnectivity();
-                            VpnService.this.wait(ONE_SECOND);
+                            VpnService.this.wait(1000); // 1 second
                         }
                     }
                 } catch (InterruptedException e) {
@@ -446,32 +378,6 @@
         }
     }
 
-    private Object startTimer(final int milliseconds, final Runnable task) {
-        Thread thread = new Thread(new Runnable() {
-            public void run() {
-                Log.d(TAG, "watchdog timer started");
-                Thread t = Thread.currentThread();
-                try {
-                    synchronized (t) {
-                        t.wait(milliseconds);
-                    }
-                    task.run();
-                } catch (InterruptedException e) {
-                    // ignored
-                }
-                Log.d(TAG, "watchdog timer stopped");
-            }
-        });
-        thread.start();
-        return thread;
-    }
-
-    private void stopTimer(Object timer) {
-        synchronized (timer) {
-            timer.notify();
-        }
-    }
-
     private String reallyGetHostIp() throws IOException {
         Enumeration<NetworkInterface> ifces =
                 NetworkInterface.getNetworkInterfaces();
@@ -487,33 +393,13 @@
         throw new IOException("Host IP is not available");
     }
 
-    private String getProfileSubpath(String subpath) throws IOException {
-        String path = getProfilePath() + subpath;
-        if (new File(path).exists()) {
-            return path;
-        } else {
-            Log.w(TAG, "Profile subpath does not exist: " + path
-                    + ", use default one");
-            String path2 = getDefaultConfigPath() + subpath;
-            if (!new File(path2).exists()) {
-                throw new IOException("Profile subpath does not exist at "
-                        + path + " or " + path2);
-            }
-            return path2;
-        }
-    }
-
-    private void sleep(int ms) {
+    protected void sleep(int ms) {
         try {
             Thread.currentThread().sleep(ms);
         } catch (InterruptedException e) {
         }
     }
 
-    private static boolean isNullOrEmpty(String message) {
-        return ((message == null) || (message.length() == 0));
-    }
-
     private InetAddress toInetAddress(int addr) throws IOException {
         byte[] aa = new byte[4];
         for (int i= 0; i < aa.length; i++) {
@@ -564,20 +450,6 @@
         }
     }
 
-    private class FileMonitor extends FileObserver {
-        private Runnable mCallback;
-
-        FileMonitor(String path, Runnable callback) {
-            super(path, CLOSE_NOWRITE);
-            mCallback = callback;
-        }
-
-        @Override
-        public void onEvent(int event, String path) {
-            if ((event & CLOSE_NOWRITE) > 0) mCallback.run();
-        }
-    }
-
     // Helper class for showing, updating notification.
     private class NotificationHelper {
         void update() {
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index c50ae94..131e156 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -29,7 +29,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.ResolveInfo;
-import android.content.pm.PackageItemInfo;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.net.Uri;
@@ -40,6 +39,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.widget.RemoteViews;
 
@@ -56,7 +56,6 @@
 
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.appwidget.IAppWidgetHost;
-import com.android.internal.util.XmlUtils;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -79,7 +78,7 @@
     static class Provider {
         int uid;
         AppWidgetProviderInfo info;
-        ArrayList<AppWidgetId> instances = new ArrayList();
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
         PendingIntent broadcast;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
         
@@ -90,7 +89,7 @@
         int uid;
         int hostId;
         String packageName;
-        ArrayList<AppWidgetId> instances = new ArrayList();
+        ArrayList<AppWidgetId> instances = new ArrayList<AppWidgetId>();
         IAppWidgetHost callbacks;
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
         
@@ -107,10 +106,10 @@
     Context mContext;
     PackageManager mPackageManager;
     AlarmManager mAlarmManager;
-    ArrayList<Provider> mInstalledProviders = new ArrayList();
+    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
     int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
-    ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList();
-    ArrayList<Host> mHosts = new ArrayList();
+    final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
+    ArrayList<Host> mHosts = new ArrayList<Host>();
     boolean mSafeMode;
 
     AppWidgetService(Context context) {
@@ -174,7 +173,7 @@
             for (int i=0; i<N; i++) {
                 AppWidgetId id = mAppWidgetIds.get(i);
                 pw.print("  ["); pw.print(i); pw.print("] id=");
-                        pw.println(id.appWidgetId);;
+                        pw.println(id.appWidgetId);
                 pw.print("    hostId=");
                         pw.print(id.host.hostId); pw.print(' ');
                         pw.print(id.host.packageName); pw.print('/');
@@ -384,7 +383,7 @@
     public List<AppWidgetProviderInfo> getInstalledProviders() {
         synchronized (mAppWidgetIds) {
             final int N = mInstalledProviders.size();
-            ArrayList<AppWidgetProviderInfo> result = new ArrayList(N);
+            ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
             for (int i=0; i<N; i++) {
                 Provider p = mInstalledProviders.get(i);
                 if (!p.zombie) {
@@ -619,7 +618,6 @@
             // rely on the fact that we've already set it and that
             // PendingIntent.getBroadcast will update the extras.
             boolean alreadyRegistered = p.broadcast != null;
-            int instancesSize = p.instances.size();
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
             intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
             intent.setComponent(p.info.provider);
@@ -695,10 +693,16 @@
 
             TypedArray sa = mContext.getResources().obtainAttributes(attrs,
                     com.android.internal.R.styleable.AppWidgetProviderInfo);
-            info.minWidth = sa.getDimensionPixelSize(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth, 0);
-            info.minHeight = sa.getDimensionPixelSize(
-                    com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight, 0);
+            
+            // These dimensions has to be resolved in the application's context.
+            // We simply send back the raw complex data, which will be
+            // converted to dp in {@link AppWidgetManager#getAppWidgetInfo}.
+            TypedValue value = sa.peekValue(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_minWidth);
+            info.minWidth = value != null ? value.data : 0; 
+            value = sa.peekValue(com.android.internal.R.styleable.AppWidgetProviderInfo_minHeight);
+            info.minHeight = value != null ? value.data : 0;
+                    
             info.updatePeriodMillis = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_updatePeriodMillis, 0);
             info.initialLayout = sa.getResourceId(
@@ -773,10 +777,12 @@
         if (real.exists()) {
             readStateFromFileLocked(real);
             if (temp.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 temp.delete();
             }
         } else if (temp.exists()) {
             readStateFromFileLocked(temp);
+            //noinspection ResultOfMethodCallIgnored
             temp.renameTo(real);
         }
     }
@@ -792,18 +798,23 @@
             // use the temporary one until it's fully written, create an empty file
             // for real, which will we'll shortly delete.
             try {
+                //noinspection ResultOfMethodCallIgnored
                 real.createNewFile();
             } catch (IOException e) {
+                // Ignore
             }
         }
 
         if (temp.exists()) {
+            //noinspection ResultOfMethodCallIgnored
             temp.delete();
         }
 
         writeStateToFileLocked(temp);
 
+        //noinspection ResultOfMethodCallIgnored
         real.delete();
+        //noinspection ResultOfMethodCallIgnored
         temp.renameTo(real);
     }
 
@@ -866,8 +877,10 @@
                     stream.close();
                 }
             } catch (IOException ex) {
+                // Ignore
             }
             if (file.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 file.delete();
             }
         }
@@ -885,7 +898,7 @@
 
             int type;
             int providerIndex = 0;
-            HashMap<Integer,Provider> loadedProviders = new HashMap();
+            HashMap<Integer,Provider> loadedProviders = new HashMap<Integer, Provider>();
             do {
                 type = parser.next();
                 if (type == XmlPullParser.START_TAG) {
@@ -986,6 +999,7 @@
                 stream.close();
             }
         } catch (IOException e) {
+            // Ignore
         }
 
         if (success) {
@@ -1081,7 +1095,7 @@
     // TODO: If there's a better way of matching an intent filter against the
     // packages for a given package, use that.
     void updateProvidersForPackageLocked(String pkgName) {
-        HashSet<String> keep = new HashSet();
+        HashSet<String> keep = new HashSet<String>();
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent,
                 PackageManager.GET_META_DATA);
@@ -1103,7 +1117,6 @@
                     if (parsed != null) {
                         keep.add(ai.name);
                         // Use the new AppWidgetProviderInfo.
-                        AppWidgetProviderInfo oldInfo = p.info;
                         p.info = parsed.info;
                         // If it's enabled
                         final int M = p.instances.size();
diff --git a/services/java/com/android/server/AttributeCache.java b/services/java/com/android/server/AttributeCache.java
index 459ae52..81378dc 100644
--- a/services/java/com/android/server/AttributeCache.java
+++ b/services/java/com/android/server/AttributeCache.java
@@ -17,56 +17,36 @@
 
 package com.android.server;
 
-import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.provider.Settings;
-import android.util.Config;
-import android.util.Log;
+import android.util.SparseArray;
 
+import java.util.HashMap;
 import java.util.WeakHashMap;
 
-public final class AttributeCache extends BroadcastReceiver {
+/**
+ * TODO: This should be better integrated into the system so it doesn't need
+ * special calls from the activity manager to clear it.
+ */
+public final class AttributeCache {
     private static AttributeCache sInstance = null;
     
     private final Context mContext;
-    private final WeakHashMap<Key, Entry> mMap =
-            new WeakHashMap<Key, Entry>();
-    private final WeakHashMap<String, Context> mContexts =
-            new WeakHashMap<String, Context>();
+    private final WeakHashMap<String, Package> mPackages =
+            new WeakHashMap<String, Package>();
+    private final Configuration mConfiguration = new Configuration();
     
-    final static class Key {
-        public final String packageName;
-        public final int resId;
-        public final int[] styleable;
+    public final static class Package {
+        public final Context context;
+        private final SparseArray<HashMap<int[], Entry>> mMap
+                = new SparseArray<HashMap<int[], Entry>>();
         
-        public Key(String inPackageName, int inResId, int[] inStyleable) {
-            packageName = inPackageName;
-            resId = inResId;
-            styleable = inStyleable;
-        }
-        
-        @Override public boolean equals(Object obj) {
-            try {
-                if (obj != null) {
-                    Key other = (Key)obj;
-                    return packageName.equals(other.packageName)
-                            && resId == other.resId
-                            && styleable == other.styleable;
-                }
-            } catch (ClassCastException e) {
-            }
-            return false;
-        }
-
-        @Override public int hashCode() {
-            return packageName.hashCode() + resId;
+        public Package(Context c) {
+            context = c;
         }
     }
     
@@ -94,36 +74,68 @@
         mContext = context;
     }
     
+    public void removePackage(String packageName) {
+        synchronized (this) {
+            mPackages.remove(packageName);
+        }
+    }
+    
+    public void updateConfiguration(Configuration config) {
+        synchronized (this) {
+            int changes = mConfiguration.updateFrom(config);
+            if ((changes & ~(ActivityInfo.CONFIG_FONT_SCALE |
+                    ActivityInfo.CONFIG_KEYBOARD_HIDDEN |
+                    ActivityInfo.CONFIG_ORIENTATION)) != 0) {
+                // The configurations being masked out are ones that commonly
+                // change so we don't want flushing the cache... all others
+                // will flush the cache.
+                mPackages.clear();
+            }
+        }
+    }
+    
     public Entry get(String packageName, int resId, int[] styleable) {
         synchronized (this) {
-            Key key = new Key(packageName, resId, styleable);
-            Entry ent = mMap.get(key);
-            if (ent != null) {
-                return ent;
-            }
-            Context context = mContexts.get(packageName);
-            if (context == null) {
+            Package pkg = mPackages.get(packageName);
+            HashMap<int[], Entry> map = null;
+            Entry ent = null;
+            if (pkg != null) {
+                map = pkg.mMap.get(resId);
+                if (map != null) {
+                    ent = map.get(styleable);
+                    if (ent != null) {
+                        return ent;
+                    }
+                }
+            } else {
+                Context context;
                 try {
                     context = mContext.createPackageContext(packageName, 0);
                     if (context == null) {
                         return null;
                     }
-                    mContexts.put(packageName, context);
                 } catch (PackageManager.NameNotFoundException e) {
                     return null;
                 }
+                pkg = new Package(context);
+                mPackages.put(packageName, pkg);
             }
+            
+            if (map == null) {
+                map = new HashMap<int[], Entry>();
+                pkg.mMap.put(resId, map);
+            }
+            
             try {
-                ent = new Entry(context,
-                        context.obtainStyledAttributes(resId, styleable));
-                mMap.put(key, ent);
+                ent = new Entry(pkg.context,
+                        pkg.context.obtainStyledAttributes(resId, styleable));
+                map.put(styleable, ent);
             } catch (Resources.NotFoundException e) {
                 return null;
             }
+            
             return ent;
         }
     }
-    @Override public void onReceive(Context context, Intent intent) {
-    }
 }
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index c67f0b5..953e401 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -33,6 +33,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.net.Uri;
+import android.provider.Settings;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -42,7 +43,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -74,12 +74,9 @@
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
 
-    // Persistent properties
-    private static final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans";
-    private static final String BACKUP_ENABLED_PROPERTY = "persist.service.bkup.enabled";
-
-    // Default time to wait after data changes before we back up the data
-    private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+    // How often we perform a backup pass.  Privileged external callers can
+    // trigger an immediate pass.
+    private static final long BACKUP_INTERVAL = 60 * 60 * 1000;
 
     private static final int MSG_RUN_BACKUP = 1;
     private static final int MSG_RUN_FULL_BACKUP = 2;
@@ -164,9 +161,8 @@
         mActivityManager = ActivityManagerNative.getDefault();
 
         // Set up our bookkeeping
-        // !!! STOPSHIP: make this disabled by default so that we then gate on
-        //               setupwizard or other opt-out UI
-        mEnabled = SystemProperties.getBoolean(BACKUP_ENABLED_PROPERTY, true);
+        mEnabled = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.BACKUP_ENABLED, 0) != 0;
         mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
         mDataDir = Environment.getDownloadCacheDirectory();
 
@@ -190,9 +186,11 @@
         registerTransport(localName.flattenToShortString(), mLocalTransport);
 
         mGoogleTransport = null;
-        // !!! TODO: set up the default transport name "the right way"
-        mCurrentTransport = SystemProperties.get(BACKUP_TRANSPORT_PROPERTY,
-                "com.google.android.backup/.BackupTransportService");
+        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT);
+        if ("".equals(mCurrentTransport)) {
+            mCurrentTransport = null;
+        }
         if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
 
         // Attach to the Google backup transport.  When this comes up, it will set
@@ -204,7 +202,7 @@
         context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
 
         // Now that we know about valid backup participants, parse any
-        // leftover journal files and schedule a new backup pass
+        // leftover journal files into the pending backup set
         parseLeftoverJournals();
 
         // Register for broadcasts about package install, etc., so we can
@@ -214,7 +212,13 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addDataScheme("package");
         mContext.registerReceiver(mBroadcastReceiver, filter);
-    }
+
+        // Schedule the first backup pass -- okay because no other threads are
+        // running yet
+        if (mEnabled) {
+            scheduleBackupPassLocked(BACKUP_INTERVAL);
+        }
+}
 
     private void makeJournalLocked() {
         try {
@@ -336,35 +340,39 @@
                 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
                 File oldJournal = mJournal;
                 synchronized (mQueueLock) {
-                    if (mPendingBackups.size() == 0) {
-                        Log.v(TAG, "Backup requested but nothing pending");
-                        break;
-                    }
-
-                    for (BackupRequest b: mPendingBackups.values()) {
-                        queue.add(b);
-                    }
-                    Log.v(TAG, "clearing pending backups");
-                    mPendingBackups.clear();
-
-                    // Start a new backup-queue journal file too
-                    if (mJournalStream != null) {
-                        try {
-                            mJournalStream.close();
-                        } catch (IOException e) {
-                            // don't need to do anything
+                    // Do we have any work to do?
+                    if (mPendingBackups.size() > 0) {
+                        for (BackupRequest b: mPendingBackups.values()) {
+                            queue.add(b);
                         }
-                        makeJournalLocked();
-                    }
+                        Log.v(TAG, "clearing pending backups");
+                        mPendingBackups.clear();
 
-                    // At this point, we have started a new journal file, and the old
-                    // file identity is being passed to the backup processing thread.
-                    // When it completes successfully, that old journal file will be
-                    // deleted.  If we crash prior to that, the old journal is parsed
-                    // at next boot and the journaled requests fulfilled.
+                        // Start a new backup-queue journal file too
+                        if (mJournalStream != null) {
+                            try {
+                                mJournalStream.close();
+                            } catch (IOException e) {
+                                // don't need to do anything
+                            }
+                            makeJournalLocked();
+                        }
+
+                        // At this point, we have started a new journal file, and the old
+                        // file identity is being passed to the backup processing thread.
+                        // When it completes successfully, that old journal file will be
+                        // deleted.  If we crash prior to that, the old journal is parsed
+                        // at next boot and the journaled requests fulfilled.
+                        (new PerformBackupThread(transport, queue, oldJournal)).start();
+                    } else {
+                        Log.v(TAG, "Backup requested but nothing pending");
+                    }
                 }
 
-                (new PerformBackupThread(transport, queue, oldJournal)).start();
+                // Schedule the next pass.
+                synchronized (mQueueLock) {
+                    scheduleBackupPassLocked(BACKUP_INTERVAL);
+                }
                 break;
             }
 
@@ -374,6 +382,7 @@
             case MSG_RUN_RESTORE:
             {
                 RestoreParams params = (RestoreParams)msg.obj;
+                Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
                 (new PerformRestoreThread(params.transport, params.observer, params.token)).start();
                 break;
             }
@@ -626,6 +635,9 @@
         public void run() {
             if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
 
+            // Backups run at background priority
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
             // The package manager doesn't have a proper <application> etc, but since
             // it's running here in the system process we can just set up its agent
             // directly and use a synthetic BackupRequest.  We always run this pass
@@ -817,6 +829,7 @@
         PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
                 long restoreSetToken) {
             mTransport = transport;
+            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
             mObserver = observer;
             mToken = restoreSetToken;
 
@@ -830,7 +843,8 @@
 
         @Override
         public void run() {
-            if (DEBUG) Log.v(TAG, "Beginning restore process");
+            if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
+                    + " mObserver=" + mObserver + " mToken=" + mToken);
             /**
              * Restore sequence:
              *
@@ -991,6 +1005,8 @@
                     Log.e(TAG, "Error finishing restore", e);
                 }
 
+                Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
                 if (mObserver != null) {
                     try {
                         mObserver.restoreFinished(error);
@@ -1006,6 +1022,8 @@
             // !!! TODO: actually run the restore through mTransport
             final String packageName = app.packageName;
 
+            Log.d(TAG, "processOneRestore packageName=" + packageName);
+
             // !!! TODO: get the dirs from the transport
             File backupDataName = new File(mDataDir, packageName + ".restore");
             backupDataName.delete();
@@ -1066,7 +1084,7 @@
         // If the caller does not hold the BACKUP permission, it can only request a
         // backup of its own data.
         HashSet<ApplicationInfo> targets;
-        if ((mContext.checkPermission("android.permission.BACKUP", Binder.getCallingPid(),
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
                 Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
             targets = mBackupParticipants.get(Binder.getCallingUid());
         } else {
@@ -1106,10 +1124,6 @@
                         Log.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
                     }
                 }
-                // Schedule a backup pass in a few minutes.  As backup-eligible data
-                // keeps changing, continue to defer the backup pass until things
-                // settle down, to avoid extra overhead.
-                scheduleBackupPassLocked(COLLECTION_INTERVAL);
             }
         } else {
             Log.w(TAG, "dataChanged but no participant pkg " + packageName);
@@ -1131,7 +1145,7 @@
     // Run a backup pass immediately for any applications that have declared
     // that they have pending updates.
     public void backupNow() throws RemoteException {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "backupNow");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow");
 
         if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
         synchronized (mQueueLock) {
@@ -1141,41 +1155,42 @@
 
     // Enable/disable the backup transport
     public void setBackupEnabled(boolean enable) {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "setBackupEnabled");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "setBackupEnabled");
 
         boolean wasEnabled = mEnabled;
         synchronized (this) {
-            SystemProperties.set(BACKUP_ENABLED_PROPERTY, enable ? "true" : "false");
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
             mEnabled = enable;
         }
 
-        if (enable && !wasEnabled) {
-            synchronized (mQueueLock) {
-                if (mPendingBackups.size() > 0) {
-                    // !!! TODO: better policy around timing of the first backup pass
-                    if (DEBUG) Log.v(TAG, "Backup enabled with pending data changes, scheduling");
-                    this.scheduleBackupPassLocked(COLLECTION_INTERVAL);
-                }
+        synchronized (mQueueLock) {
+            if (enable && !wasEnabled) {
+                // if we've just been enabled, start scheduling backup passes
+                scheduleBackupPassLocked(BACKUP_INTERVAL);
+            } else if (!enable) {
+                // No longer enabled, so stop running backups.
+                mBackupHandler.removeMessages(MSG_RUN_BACKUP);
             }
         }
-}
+    }
 
     // Report whether the backup mechanism is currently enabled
     public boolean isBackupEnabled() {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "isBackupEnabled");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
         return mEnabled;    // no need to synchronize just to read it
     }
 
     // Report the name of the currently active transport
     public String getCurrentTransport() {
         mContext.enforceCallingPermission("android.permission.BACKUP", "getCurrentTransport");
-        Log.v(TAG, "getCurrentTransport() returning " + mCurrentTransport);
+        Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
         return mCurrentTransport;
     }
 
     // Report all known, available backup transports
     public String[] listAllTransports() {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "listAllTransports");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "listAllTransports");
 
         String[] list = null;
         ArrayList<String> known = new ArrayList<String>();
@@ -1196,14 +1211,15 @@
     // name is not one of the available transports, no action is taken and the method
     // returns null.
     public String selectBackupTransport(String transport) {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
 
         synchronized (mTransports) {
             String prevTransport = null;
             if (mTransports.get(transport) != null) {
                 prevTransport = mCurrentTransport;
                 mCurrentTransport = transport;
-                SystemProperties.set(BACKUP_TRANSPORT_PROPERTY, transport);
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_TRANSPORT, transport);
                 Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
                         + " returning " + prevTransport);
             } else {
@@ -1249,7 +1265,7 @@
 
     // Hand off a restore session
     public IRestoreSession beginRestoreSession(String transport) {
-        mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
 
         synchronized(this) {
             if (mActiveRestoreSession != null) {
@@ -1275,7 +1291,7 @@
 
         // --- Binder interface ---
         public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP",
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                     "getAvailableRestoreSets");
 
             try {
@@ -1294,7 +1310,9 @@
 
         public int performRestore(long token, IRestoreObserver observer)
                 throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore");
+
+            Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
 
             if (mRestoreSets != null) {
                 for (int i = 0; i < mRestoreSets.length; i++) {
@@ -1312,9 +1330,11 @@
         }
 
         public void endRestoreSession() throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP",
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                     "endRestoreSession");
 
+            Log.d(TAG, "endRestoreSession");
+
             mRestoreTransport.finishRestore();
             mRestoreTransport = null;
             synchronized(BackupManagerService.this) {
@@ -1331,13 +1351,22 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mQueueLock) {
+            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled"));
+            boolean scheduled = mBackupHandler.hasMessages(MSG_RUN_BACKUP);
+            if (scheduled != mEnabled) {
+                if (mEnabled) {
+                    pw.println("ERROR: backups enabled but none scheduled!");
+                } else {
+                    pw.println("ERROR: backups are scheduled but not enabled!");
+                }
+            }
             pw.println("Available transports:");
             for (String t : listAllTransports()) {
                 String pad = (t.equals(mCurrentTransport)) ? "  * " : "    ";
                 pw.println(pad + t);
             }
             int N = mBackupParticipants.size();
-            pw.println("Participants:");
+            pw.println("Participants: " + N);
             for (int i=0; i<N; i++) {
                 int uid = mBackupParticipants.keyAt(i);
                 pw.print("  uid: ");
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 596053d..5cdce5b 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -92,6 +92,7 @@
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
+    private static final int BATTERY_LEVEL_CLOSE_WARNING = 20;
     private static final int BATTERY_LEVEL_WARNING = 15;
 
     private final Context mContext;
@@ -122,6 +123,7 @@
     private long mDischargeStartTime;
     private int mDischargeStartLevel;
     
+    private boolean mSentLowBatteryBroadcast = false;
     
     public BatteryService(Context context) {
         mContext = context;
@@ -286,7 +288,11 @@
 
             sendIntent();
             if (sendBatteryLow) {
+                mSentLowBatteryBroadcast = true;
                 mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
+            } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= BATTERY_LEVEL_CLOSE_WARNING) {
+                mSentLowBatteryBroadcast = false;
+                mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
             }
             
             // This needs to be done after sendIntent() so that we get the lastest battery stats.
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 5bc9b5f..7597f85 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -37,6 +37,9 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import java.util.LinkedList;
+import java.util.ListIterator;
+
 public class HardwareService extends IHardwareService.Stub {
     private static final String TAG = "HardwareService";
 
@@ -50,9 +53,62 @@
     static final int LIGHT_FLASH_NONE = 0;
     static final int LIGHT_FLASH_TIMED = 1;
 
+    private final LinkedList<Vibration> mVibrations;
+    private Vibration mCurrentVibration;
+
     private boolean mAttentionLightOn;
     private boolean mPulsing;
 
+    private class Vibration implements IBinder.DeathRecipient {
+        private final IBinder mToken;
+        private final long    mTimeout;
+        private final long    mStartTime;
+        private final long[]  mPattern;
+        private final int     mRepeat;
+
+        Vibration(IBinder token, long millis) {
+            this(token, millis, null, 0);
+        }
+
+        Vibration(IBinder token, long[] pattern, int repeat) {
+            this(token, 0, pattern, repeat);
+        }
+
+        private Vibration(IBinder token, long millis, long[] pattern,
+                int repeat) {
+            mToken = token;
+            mTimeout = millis;
+            mStartTime = SystemClock.uptimeMillis();
+            mPattern = pattern;
+            mRepeat = repeat;
+        }
+
+        public void binderDied() {
+            synchronized (mVibrations) {
+                mVibrations.remove(this);
+                if (this == mCurrentVibration) {
+                    doCancelVibrateLocked();
+                    startNextVibrationLocked();
+                }
+            }
+        }
+
+        public boolean hasLongerTimeout(long millis) {
+            if (mTimeout == 0) {
+                // This is a pattern, return false to play the simple
+                // vibration.
+                return false;
+            }
+            if ((mStartTime + mTimeout)
+                    < (SystemClock.uptimeMillis() + millis)) {
+                // If this vibration will end before the time passed in, let
+                // the new vibration play.
+                return false;
+            }
+            return true;
+        }
+    }
+
     HardwareService(Context context) {
         // Reset the hardware to a default state, in case this is a runtime
         // restart instead of a fresh boot.
@@ -66,6 +122,8 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         mWakeLock.setReferenceCounted(true);
 
+        mVibrations = new LinkedList<Vibration>();
+
         mBatteryStats = BatteryStatsService.getService();
         
         IntentFilter filter = new IntentFilter();
@@ -78,13 +136,24 @@
         super.finalize();
     }
 
-    public void vibrate(long milliseconds) {
+    public void vibrate(long milliseconds, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
         }
-        doCancelVibrate();
-        vibratorOn(milliseconds);
+        if (mCurrentVibration != null
+                && mCurrentVibration.hasLongerTimeout(milliseconds)) {
+            // Ignore this vibration since the current vibration will play for
+            // longer than milliseconds.
+            return;
+        }
+        Vibration vib = new Vibration(token, milliseconds);
+        synchronized (mVibrations) {
+            removeVibrationLocked(token);
+            doCancelVibrateLocked();
+            mCurrentVibration = vib;
+            startVibrationLocked(vib);
+        }
     }
 
     private boolean isAll0(long[] pattern) {
@@ -121,34 +190,25 @@
                 return;
             }
 
-            synchronized (this) {
-                Death death = new Death(token);
-                try {
-                    token.linkToDeath(death, 0);
-                } catch (RemoteException e) {
-                    return;
+            Vibration vib = new Vibration(token, pattern, repeat);
+            try {
+                token.linkToDeath(vib, 0);
+            } catch (RemoteException e) {
+                return;
+            }
+
+            synchronized (mVibrations) {
+                removeVibrationLocked(token);
+                doCancelVibrateLocked();
+                if (repeat >= 0) {
+                    mVibrations.addFirst(vib);
+                    startNextVibrationLocked();
+                } else {
+                    // A negative repeat means that this pattern is not meant
+                    // to repeat. Treat it like a simple vibration.
+                    mCurrentVibration = vib;
+                    startVibrationLocked(vib);
                 }
-
-                Thread oldThread = mThread;
-
-                if (oldThread != null) {
-                    // stop the old one
-                    synchronized (mThread) {
-                        mThread.mDone = true;
-                        mThread.notify();
-                    }
-                }
-
-                if (mDeath != null) {
-                    mToken.unlinkToDeath(mDeath, 0);
-                }
-
-                mDeath = death;
-                mToken = token;
-
-                // start the new thread
-                mThread = new VibrateThread(pattern, repeat);
-                mThread.start();
             }
         }
         finally {
@@ -156,7 +216,7 @@
         }
     }
 
-    public void cancelVibrate() {
+    public void cancelVibrate(IBinder token) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.VIBRATE,
                 "cancelVibrate");
@@ -164,7 +224,13 @@
         // so wakelock calls will succeed
         long identity = Binder.clearCallingIdentity();
         try {
-            doCancelVibrate();
+            synchronized (mVibrations) {
+                final Vibration vib = removeVibrationLocked(token);
+                if (vib == mCurrentVibration) {
+                    doCancelVibrateLocked();
+                    startNextVibrationLocked();
+                }
+            }
         }
         finally {
             Binder.restoreCallingIdentity(identity);
@@ -277,27 +343,74 @@
         }
     };
 
-    private void doCancelVibrate() {
-        synchronized (this) {
-            if (mThread != null) {
-                synchronized (mThread) {
-                    mThread.mDone = true;
-                    mThread.notify();
-                }
-                mThread = null;
+    private final Runnable mVibrationRunnable = new Runnable() {
+        public void run() {
+            synchronized (mVibrations) {
+                doCancelVibrateLocked();
+                startNextVibrationLocked();
             }
-            vibratorOff();
+        }
+    };
+
+    // Lock held on mVibrations
+    private void doCancelVibrateLocked() {
+        if (mThread != null) {
+            synchronized (mThread) {
+                mThread.mDone = true;
+                mThread.notify();
+            }
+            mThread = null;
+        }
+        vibratorOff();
+        mH.removeCallbacks(mVibrationRunnable);
+    }
+
+    // Lock held on mVibrations
+    private void startNextVibrationLocked() {
+        if (mVibrations.size() <= 0) {
+            return;
+        }
+        mCurrentVibration = mVibrations.getFirst();
+        startVibrationLocked(mCurrentVibration);
+    }
+
+    // Lock held on mVibrations
+    private void startVibrationLocked(final Vibration vib) {
+        if (vib.mTimeout != 0) {
+            vibratorOn(vib.mTimeout);
+            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
+        } else {
+            // mThread better be null here. doCancelVibrate should always be
+            // called before startNextVibrationLocked or startVibrationLocked.
+            mThread = new VibrateThread(vib);
+            mThread.start();
         }
     }
 
+    // Lock held on mVibrations
+    private Vibration removeVibrationLocked(IBinder token) {
+        ListIterator<Vibration> iter = mVibrations.listIterator(0);
+        while (iter.hasNext()) {
+            Vibration vib = iter.next();
+            if (vib.mToken == token) {
+                iter.remove();
+                return vib;
+            }
+        }
+        // We might be looking for a simple vibration which is only stored in
+        // mCurrentVibration.
+        if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
+            return mCurrentVibration;
+        }
+        return null;
+    }
+
     private class VibrateThread extends Thread {
-        long[] mPattern;
-        int mRepeat;
+        final Vibration mVibration;
         boolean mDone;
     
-        VibrateThread(long[] pattern, int repeat) {
-            mPattern = pattern;
-            mRepeat = repeat;
+        VibrateThread(Vibration vib) {
+            mVibration = vib;
             mWakeLock.acquire();
         }
 
@@ -323,8 +436,9 @@
             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
             synchronized (this) {
                 int index = 0;
-                long[] pattern = mPattern;
+                long[] pattern = mVibration.mPattern;
                 int len = pattern.length;
+                int repeat = mVibration.mRepeat;
                 long duration = 0;
 
                 while (!mDone) {
@@ -347,50 +461,37 @@
                             HardwareService.this.vibratorOn(duration);
                         }
                     } else {
-                        if (mRepeat < 0) {
+                        if (repeat < 0) {
                             break;
                         } else {
-                            index = mRepeat;
+                            index = repeat;
                             duration = 0;
                         }
                     }
                 }
-                if (mDone) {
-                    // make sure vibrator is off if we were cancelled.
-                    // otherwise, it will turn off automatically 
-                    // when the last timeout expires.
-                    HardwareService.this.vibratorOff();
-                }
                 mWakeLock.release();
             }
-            synchronized (HardwareService.this) {
+            synchronized (mVibrations) {
                 if (mThread == this) {
                     mThread = null;
                 }
+                if (!mDone) {
+                    // If this vibration finished naturally, start the next
+                    // vibration.
+                    mVibrations.remove(mVibration);
+                    startNextVibrationLocked();
+                }
             }
         }
     };
 
-    private class Death implements IBinder.DeathRecipient {
-        IBinder mMe;
-
-        Death(IBinder me) {
-            mMe = me;
-        }
-
-        public void binderDied() {
-            synchronized (HardwareService.this) {
-                if (mMe == mToken) {
-                    doCancelVibrate();
-                }
-            }
-        }
-    }
-
     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
-                doCancelVibrate();
+                synchronized (mVibrations) {
+                    doCancelVibrateLocked();
+                    mVibrations.clear();
+                }
             }
         }
     };
@@ -407,8 +508,6 @@
     private final IBatteryStats mBatteryStats;
     
     volatile VibrateThread mThread;
-    volatile Death mDeath;
-    volatile IBinder mToken;
 
     private int mNativePointer;
 
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 3fc1e0e..9b0a2d4 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -35,6 +35,7 @@
  */
 class HeadsetObserver extends UEventObserver {
     private static final String TAG = HeadsetObserver.class.getSimpleName();
+    private static final boolean LOG = false;
 
     private static final String HEADSET_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/h2w";
     private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
@@ -61,7 +62,7 @@
 
     @Override
     public void onUEvent(UEventObserver.UEvent event) {
-        Log.v(TAG, "Headset UEVENT: " + event.toString());
+        if (LOG) Log.v(TAG, "Headset UEVENT: " + event.toString());
 
         try {
             update(event.get("SWITCH_NAME"), Integer.parseInt(event.get("SWITCH_STATE")));
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index fc37290..0f5b3fd 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -507,6 +507,7 @@
 
     private void removeProvider(LocationProviderProxy provider) {
         mProviders.remove(provider);
+        provider.unlinkProvider();
         mProvidersByName.remove(provider.getName());
     }
 
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 4a2808b..854138c 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -25,15 +25,20 @@
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.media.AsyncPlayer;
 import android.media.AudioManager;
 import android.net.Uri;
@@ -88,6 +93,12 @@
     private NotificationRecord mVibrateNotification;
     private Vibrator mVibrator = new Vibrator();
 
+    // adb
+    private int mBatteryPlugged;
+    private boolean mAdbEnabled = false;
+    private boolean mAdbNotificationShown = false;
+    private Notification mAdbNotification;
+    
     private ArrayList<NotificationRecord> mNotificationList;
 
     private ArrayList<ToastRecord> mToastQueue;
@@ -297,6 +308,9 @@
                     mBatteryFull = batteryFull;
                     updateLights();
                 }
+                
+                mBatteryPlugged = intent.getIntExtra("plugged", 0);
+                updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
                 Uri uri = intent.getData();
@@ -312,6 +326,31 @@
         }
     };
 
+    class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+        
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.ADB_ENABLED), false, this);
+            update();
+        }
+
+        @Override public void onChange(boolean selfChange) {
+            update();
+        }
+
+        public void update() {
+            ContentResolver resolver = mContext.getContentResolver();
+            mAdbEnabled = Settings.Secure.getInt(resolver,
+                        Settings.Secure.ADB_ENABLED, 0) != 0;
+            updateAdbNotification();
+        }
+    }
+    private final SettingsObserver mSettingsObserver;
+    
     NotificationManagerService(Context context, StatusBarService statusBar,
             HardwareService hardware)
     {
@@ -333,6 +372,9 @@
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         mContext.registerReceiver(mIntentReceiver, filter);
+        
+        mSettingsObserver = new SettingsObserver(mHandler);
+        mSettingsObserver.observe();
     }
 
     // Toasts
@@ -892,6 +934,62 @@
         return -1;
     }
 
+    // This is here instead of StatusBarPolicy because it is an important
+    // security feature that we don't want people customizing the platform
+    // to accidentally lose.
+    private void updateAdbNotification() {
+        if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+            if (!mAdbNotificationShown) {
+                NotificationManager notificationManager = (NotificationManager) mContext
+                        .getSystemService(Context.NOTIFICATION_SERVICE);
+                if (notificationManager != null) {
+                    Resources r = mContext.getResources();
+                    CharSequence title = r.getText(
+                            com.android.internal.R.string.adb_active_notification_title);
+                    CharSequence message = r.getText(
+                            com.android.internal.R.string.adb_active_notification_message);
+
+                    if (mAdbNotification == null) {
+                        mAdbNotification = new Notification();
+                        mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
+                        mAdbNotification.when = 0;
+                        mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT;
+                        mAdbNotification.tickerText = title;
+                        mAdbNotification.defaults |= Notification.DEFAULT_SOUND;
+                    }
+
+                    Intent intent = new Intent(
+                            Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                    // Note: we are hard-coding the component because this is
+                    // an important security UI that we don't want anyone
+                    // intercepting.
+                    intent.setComponent(new ComponentName("com.android.settings",
+                            "com.android.settings.DevelopmentSettings"));
+                    PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+                            intent, 0);
+
+                    mAdbNotification.setLatestEventInfo(mContext, title, message, pi);
+                    
+                    mAdbNotificationShown = true;
+                    notificationManager.notify(
+                            com.android.internal.R.string.adb_active_notification_title,
+                            mAdbNotification);
+                }
+            }
+            
+        } else if (mAdbNotificationShown) {
+            NotificationManager notificationManager = (NotificationManager) mContext
+                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            if (notificationManager != null) {
+                mAdbNotificationShown = false;
+                notificationManager.cancel(
+                        com.android.internal.R.string.adb_active_notification_title);
+            }
+        }
+    }
+    
     // ======================================================================
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 16f14e8..a1b4c268 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -59,7 +59,14 @@
 
     private List<PackageInfo> mAllPackages;
     private PackageManager mPackageManager;
+    // version & signature info of each app in a restore set
     private HashMap<String, Metadata> mRestoredSignatures;
+    // The version info of each backed-up app as read from the state file
+    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
+
+    private final HashSet<String> mExisting = new HashSet<String>();
+    private int mStoredSdkVersion;
+    private String mStoredIncrementalVersion;
 
     public class Metadata {
         public int versionCode;
@@ -96,24 +103,39 @@
 
         ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these
         DataOutputStream outWriter = new DataOutputStream(bufStream);
-        HashSet<String> existing = parseStateFile(oldState);
+        parseStateFile(oldState);
+
+        // If the stored version string differs, we need to re-backup all
+        // of the metadata.  We force this by removing everything from the
+        // "already backed up" map built by parseStateFile().
+        if (mStoredIncrementalVersion == null
+                || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
+            Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
+                    + Build.VERSION.INCREMENTAL + " - rewriting");
+            mExisting.clear();
+        }
 
         try {
             /*
              * Global metadata:
              *
-             * int version -- the SDK version of the OS itself on the device
-             *                that produced this backup set.  Used to reject
-             *                backups from later OSes onto earlier ones.
+             * int SDKversion -- the SDK version of the OS itself on the device
+             *                   that produced this backup set.  Used to reject
+             *                   backups from later OSes onto earlier ones.
+             * String incremental -- the incremental release name of the OS stored in
+             *                       the backup set.
              */
-            if (!existing.contains(GLOBAL_METADATA_KEY)) {
+            if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
                 if (DEBUG) Log.v(TAG, "Storing global metadata key");
                 outWriter.writeInt(Build.VERSION.SDK_INT);
+                outWriter.writeUTF(Build.VERSION.INCREMENTAL);
                 byte[] metadata = bufStream.toByteArray();
                 data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
                 data.writeEntityData(metadata, metadata.length);
             } else {
                 if (DEBUG) Log.v(TAG, "Global metadata key already stored");
+                // don't consider it to have been skipped/deleted
+                mExisting.remove(GLOBAL_METADATA_KEY);
             }
 
             // For each app we have on device, see if we've backed it up yet.  If not,
@@ -123,11 +145,36 @@
                 if (packName.equals(GLOBAL_METADATA_KEY)) {
                     // We've already handled the metadata key; skip it here
                     continue;
-                } else if (!existing.contains(packName)) {
-                    // We haven't stored this app's signatures yet, so we do that now
+                } else {
+                    PackageInfo info = null;
                     try {
-                        PackageInfo info = mPackageManager.getPackageInfo(packName,
+                        info = mPackageManager.getPackageInfo(packName,
                                 PackageManager.GET_SIGNATURES);
+                    } catch (NameNotFoundException e) {
+                        // Weird; we just found it, and now are told it doesn't exist.
+                        // Treat it as having been removed from the device.
+                        mExisting.add(packName);
+                        continue;
+                    }
+
+                    boolean doBackup = false;
+                    if (!mExisting.contains(packName)) {
+                        // We haven't backed up this app before
+                        doBackup = true;
+                    } else {
+                        // We *have* backed this one up before.  Check whether the version
+                        // of the backup matches the version of the current app; if they
+                        // don't match, the app has been updated and we need to store its
+                        // metadata again.  In either case, take it out of mExisting so that
+                        // we don't consider it deleted later.
+                        if (info.versionCode != mStateVersions.get(packName).versionCode) {
+                            doBackup = true;
+                        }
+                        mExisting.remove(packName);
+                    }
+
+                    if (doBackup) {
+                        // We need to store this app's metadata
                         /*
                          * Metadata for each package:
                          *
@@ -135,7 +182,7 @@
                          * byte[] signatures -- [len] flattened Signature[] of the package
                          */
 
-                        // marshall the version code in a canonical form
+                        // marshal the version code in a canonical form
                         bufStream.reset();
                         outWriter.writeInt(info.versionCode);
                         byte[] versionBuf = bufStream.toByteArray();
@@ -153,18 +200,6 @@
                         data.writeEntityHeader(packName, versionBuf.length + sigs.length);
                         data.writeEntityData(versionBuf, versionBuf.length);
                         data.writeEntityData(sigs, sigs.length);
-                    } catch (NameNotFoundException e) {
-                        // Weird; we just found it, and now are told it doesn't exist.
-                        // Treat it as having been removed from the device.
-                        existing.add(packName);
-                    }
-                } else {
-                    // We've already backed up this app.  Remove it from the set so
-                    // we can tell at the end what has disappeared from the device.
-                    // !!! TODO: take out the debugging message
-                    if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName);
-                    if (!existing.remove(packName)) {
-                        Log.d(TAG, "*** failed to remove " + packName + " from package set!");
                     }
                 }
             }
@@ -172,7 +207,7 @@
             // At this point, the only entries in 'existing' are apps that were
             // mentioned in the saved state file, but appear to no longer be present
             // on the device.  Write a deletion entity for them.
-            for (String app : existing) {
+            for (String app : mExisting) {
                 // !!! TODO: take out this msg
                 if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
                 try {
@@ -215,17 +250,21 @@
             DataInputStream in = new DataInputStream(baStream);
 
             if (key.equals(GLOBAL_METADATA_KEY)) {
-                storedSystemVersion = in.readInt();
+                int storedSdkVersion = in.readInt();
                 if (DEBUG) Log.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
                 if (storedSystemVersion > Build.VERSION.SDK_INT) {
                     // returning before setting the sig map means we rejected the restore set
                     Log.w(TAG, "Restore set was from a later version of Android; not restoring");
                     return;
                 }
+                mStoredSdkVersion = storedSdkVersion;
+                mStoredIncrementalVersion = in.readUTF();
                 // !!! TODO: remove this debugging output
                 if (DEBUG) {
                     Log.i(TAG, "Restore set version " + storedSystemVersion
-                            + " is compatible with OS version " + Build.VERSION.SDK_INT);
+                            + " is compatible with OS version " + Build.VERSION.SDK_INT
+                            + " (" + mStoredIncrementalVersion + " vs "
+                            + Build.VERSION.INCREMENTAL + ")");
                 }
             } else {
                 // it's a file metadata record
@@ -302,31 +341,45 @@
     }
 
     // Util: parse out an existing state file into a usable structure
-    private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) {
-        HashSet<String> set = new HashSet<String>();
+    private void parseStateFile(ParcelFileDescriptor stateFile) {
+        mExisting.clear();
+        mStateVersions.clear();
+        mStoredSdkVersion = 0;
+        mStoredIncrementalVersion = null;
+
         // The state file is just the list of app names we have stored signatures for
+        // with the exception of the metadata block, to which is also appended the
+        // version numbers corresponding with the last time we wrote this PM block.
+        // If they mismatch the current system, we'll re-store the metadata key.
         FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
         DataInputStream in = new DataInputStream(instream);
 
         int bufSize = 256;
         byte[] buf = new byte[bufSize];
         try {
-            int nameSize = in.readInt();
-            if (bufSize < nameSize) {
-                bufSize = nameSize + 32;
-                buf = new byte[bufSize];
+            String pkg = in.readUTF();
+            if (pkg.equals(GLOBAL_METADATA_KEY)) {
+                mStoredSdkVersion = in.readInt();
+                mStoredIncrementalVersion = in.readUTF();
+                mExisting.add(GLOBAL_METADATA_KEY);
+            } else {
+                Log.e(TAG, "No global metadata in state file!");
+                return;
             }
-            in.read(buf, 0, nameSize);
-            String pkg = new String(buf, 0, nameSize);
-            set.add(pkg);
+
+            // The global metadata was first; now read all the apps
+            while (true) {
+                pkg = in.readUTF();
+                int versionCode = in.readInt();
+                mExisting.add(pkg);
+                mStateVersions.put(pkg, new Metadata(versionCode, null));
+            }
         } catch (EOFException eof) {
             // safe; we're done
         } catch (IOException e) {
             // whoops, bad state file.  abort.
-            Log.e(TAG, "Unable to read Package Manager state file");
-            return null;
+            Log.e(TAG, "Unable to read Package Manager state file: " + e);
         }
-        return set;
     }
 
     // Util: write out our new backup state file
@@ -336,15 +389,14 @@
 
         try {
             // by the time we get here we know we've stored the global metadata record
-            byte[] metaNameBuf = GLOBAL_METADATA_KEY.getBytes();
-            out.writeInt(metaNameBuf.length);
-            out.write(metaNameBuf);
+            out.writeUTF(GLOBAL_METADATA_KEY);
+            out.writeInt(Build.VERSION.SDK_INT);
+            out.writeUTF(Build.VERSION.INCREMENTAL);
 
             // now write all the app names too
             for (PackageInfo pkg : pkgs) {
-                byte[] pkgNameBuf = pkg.packageName.getBytes();
-                out.writeInt(pkgNameBuf.length);
-                out.write(pkgNameBuf);
+                out.writeUTF(pkg.packageName);
+                out.writeInt(pkg.versionCode);
             }
         } catch (IOException e) {
             Log.e(TAG, "Unable to write package manager state file!");
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 857bfaa..384d334 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -3295,6 +3295,7 @@
                 if (extras != null) {
                     intent.putExtras(extras);
                 }
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 am.broadcastIntent(
                     null, intent,
                             null, null, 0, null, null, null, false, false);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index c5ea5fa9..79d78ad1 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -709,7 +709,10 @@
                     p.awakeOnSet = true;
                 }
             } else {
-                mPokeLocks.remove(token);
+                PokeLock rLock = mPokeLocks.remove(token);
+                if (rLock != null) {
+                    token.unlinkToDeath(rLock, 0);
+                }
             }
 
             int oldPokey = mPokey;
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index e91798b..4984b19 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1883,7 +1883,9 @@
         private WifiLock removeLock(IBinder binder) {
             int index = findLockByBinder(binder);
             if (index >= 0) {
-                return mList.remove(index);
+                WifiLock ret = mList.remove(index);
+                ret.unlinkDeathRecipient();
+                return ret;
             } else {
                 return null;
             }
@@ -1995,6 +1997,10 @@
                 binderDied();
             }
         }
+
+        void unlinkDeathRecipient() {
+            mBinder.unlinkToDeath(this, 0);
+        }
     }
 
     private class Multicaster extends DeathRecipient {
@@ -2062,7 +2068,10 @@
 
     private void removeMulticasterLocked(int i, int uid)
     {
-        mMulticasters.remove(i);
+        Multicaster removed = mMulticasters.remove(i);
+        if (removed != null) {
+            removed.unlinkDeathRecipient();
+        }
         if (mMulticasters.size() == 0) {
             WifiNative.startPacketFiltering();
         }
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 8c3f61e..0c75251 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -2332,6 +2332,14 @@
             if (!mDisplayFrozen) {
                 if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
                     mNextAppTransition = transit;
+                } else if (transit == WindowManagerPolicy.TRANSIT_TASK_OPEN
+                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_TASK_CLOSE) {
+                    // Opening a new task always supersedes a close for the anim.
+                    mNextAppTransition = transit;
+                } else if (transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
+                        && mNextAppTransition == WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE) {
+                    // Opening a new activity always supersedes a close for the anim.
+                    mNextAppTransition = transit;
                 }
                 mAppTransitionReady = false;
                 mAppTransitionTimeout = false;
@@ -3414,7 +3422,10 @@
                 synchronized (mWindowMap) {
                     for (int i=0; i<mRotationWatchers.size(); i++) {
                         if (watcherBinder == mRotationWatchers.get(i).asBinder()) {
-                            mRotationWatchers.remove(i);
+                            IRotationWatcher removed = mRotationWatchers.remove(i);
+                            if (removed != null) {
+                                removed.asBinder().unlinkToDeath(this, 0);
+                            }
                             i--;
                         }
                     }
@@ -5491,6 +5502,7 @@
             } catch (RemoteException e) {
             }
             synchronized(mWindowMap) {
+                mClient.asBinder().unlinkToDeath(this, 0);
                 mClientDead = true;
                 killSessionLocked();
             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b4c7afc..ec8bf4b 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.AttributeCache;
 import com.android.server.IntentResolver;
 import com.android.server.ProcessMap;
 import com.android.server.ProcessStats;
@@ -61,7 +62,6 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -751,6 +751,8 @@
 
     int mFactoryTest;
 
+    boolean mCheckedForSetup;
+    
     /**
      * The time at which we will allow normal application switches again,
      * after a call to {@link #stopAppSwitches()}.
@@ -1780,6 +1782,12 @@
             r.stopped = true;
         }
 
+        // Launch the new version setup screen if needed.  We do this -after-
+        // launching the initial activity (that is, home), so that it can have
+        // a chance to initialize itself while in the background, making the
+        // switch back to it faster and look better.
+        startSetupActivityLocked();
+        
         return true;
     }
 
@@ -2361,6 +2369,96 @@
         }
     }
 
+    private boolean startHomeActivityLocked() {
+        if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
+                && mTopAction == null) {
+            // We are running in factory test mode, but unable to find
+            // the factory test app, so just sit around displaying the
+            // error message and don't try to start anything.
+            return false;
+        }
+        Intent intent = new Intent(
+            mTopAction,
+            mTopData != null ? Uri.parse(mTopData) : null);
+        intent.setComponent(mTopComponent);
+        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+            intent.addCategory(Intent.CATEGORY_HOME);
+        }
+        ActivityInfo aInfo =
+            intent.resolveActivityInfo(mContext.getPackageManager(),
+                    STOCK_PM_FLAGS);
+        if (aInfo != null) {
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+            // Don't do this if the home app is currently being
+            // instrumented.
+            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
+                    aInfo.applicationInfo.uid);
+            if (app == null || app.instrumentationClass == null) {
+                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivityLocked(null, intent, null, null, 0, aInfo,
+                        null, null, 0, 0, 0, false, false);
+            }
+        }
+        
+        
+        return true;
+    }
+    
+    /**
+     * Starts the "new version setup screen" if appropriate.
+     */
+    private void startSetupActivityLocked() {
+        // Only do this once per boot.
+        if (mCheckedForSetup) {
+            return;
+        }
+        
+        // We will show this screen if the current one is a different
+        // version than the last one shown, and we are not running in
+        // low-level factory test mode.
+        final ContentResolver resolver = mContext.getContentResolver();
+        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL &&
+                Settings.Secure.getInt(resolver,
+                        Settings.Secure.DEVICE_PROVISIONED, 0) != 0) {
+            mCheckedForSetup = true;
+            
+            // See if we should be showing the platform update setup UI.
+            Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
+            List<ResolveInfo> ris = mSelf.mContext.getPackageManager()
+                    .queryIntentActivities(intent, PackageManager.GET_META_DATA);
+            
+            // We don't allow third party apps to replace this.
+            ResolveInfo ri = null;
+            for (int i=0; ris != null && i<ris.size(); i++) {
+                if ((ris.get(i).activityInfo.applicationInfo.flags
+                        & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    ri = ris.get(i);
+                    break;
+                }
+            }
+            
+            if (ri != null) {
+                String vers = ri.activityInfo.metaData != null
+                        ? ri.activityInfo.metaData.getString(Intent.METADATA_SETUP_VERSION)
+                        : null;
+                if (vers == null && ri.activityInfo.applicationInfo.metaData != null) {
+                    vers = ri.activityInfo.applicationInfo.metaData.getString(
+                            Intent.METADATA_SETUP_VERSION);
+                }
+                String lastVers = Settings.Secure.getString(
+                        resolver, Settings.Secure.LAST_SETUP_SHOWN);
+                if (vers != null && !vers.equals(lastVers)) {
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.setComponent(new ComponentName(
+                            ri.activityInfo.packageName, ri.activityInfo.name));
+                    startActivityLocked(null, intent, null, null, 0, ri.activityInfo,
+                            null, null, 0, 0, 0, false, false);
+                }
+            }
+        }
+    }
+    
     /**
      * Ensure that the top activity in the stack is resumed.
      *
@@ -2382,37 +2480,7 @@
         if (next == null) {
             // There are no more activities!  Let's just start up the
             // Launcher...
-            if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL
-                    && mTopAction == null) {
-                // We are running in factory test mode, but unable to find
-                // the factory test app, so just sit around displaying the
-                // error message and don't try to start anything.
-                return false;
-            }
-            Intent intent = new Intent(
-                mTopAction,
-                mTopData != null ? Uri.parse(mTopData) : null);
-            intent.setComponent(mTopComponent);
-            if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-                intent.addCategory(Intent.CATEGORY_HOME);
-            }
-            ActivityInfo aInfo =
-                intent.resolveActivityInfo(mContext.getPackageManager(),
-                        STOCK_PM_FLAGS);
-            if (aInfo != null) {
-                intent.setComponent(new ComponentName(
-                        aInfo.applicationInfo.packageName, aInfo.name));
-                // Don't do this if the home app is currently being
-                // instrumented.
-                ProcessRecord app = getProcessRecordLocked(aInfo.processName,
-                        aInfo.applicationInfo.uid);
-                if (app == null || app.instrumentationClass == null) {
-                    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
-                    startActivityLocked(null, intent, null, null, 0, aInfo,
-                            null, null, 0, 0, 0, false, false);
-                }
-            }
-            return true;
+            return startHomeActivityLocked();
         }
 
         next.delayedResume = false;
@@ -10757,6 +10825,10 @@
                         if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
                             uninstallPackageLocked(ssp,
                                     intent.getIntExtra(Intent.EXTRA_UID, -1), false);
+                            AttributeCache ac = AttributeCache.instance();
+                            if (ac != null) {
+                                ac.removePackage(ssp);
+                            }
                         }
                     }
                 }
@@ -11807,6 +11879,11 @@
                 Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
                         null, false, false, MY_PID, Process.SYSTEM_UID);
+                
+                AttributeCache ac = AttributeCache.instance();
+                if (ac != null) {
+                    ac.updateConfiguration(mConfiguration);
+                }
             }
         }
         
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index 059dc7b..c8db95b 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -76,12 +76,8 @@
     private static StatusBarPolicy sInstance;
 
     // message codes for the handler
-    private static final int EVENT_DATA_CONN_STATE_CHANGED = 2;
-    private static final int EVENT_DATA_ACTIVITY = 3;
     private static final int EVENT_BATTERY_CLOSE = 4;
 
-    private static final int BATTERY_LEVEL_CLOSE_WARNING = 20;
-
     private final Context mContext;
     private final StatusBarService mService;
     private final Handler mHandler = new StatusBarHandler();
@@ -357,6 +353,9 @@
             else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
                 updateClock();
             }
+            else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+                updateBattery(intent);
+            }
             else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 updateClock();
             }
@@ -371,12 +370,12 @@
             else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
                 updateSyncState(intent);
             }
-            else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
-                updateBattery(intent);
-            }
             else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
                 onBatteryLow(intent);
             }
+            else if (action.equals(Intent.ACTION_BATTERY_OKAY)) {
+                onBatteryOkay(intent);
+            }
             else if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION) ||
                     action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) ||
                     action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
@@ -519,6 +518,7 @@
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(Intent.ACTION_BATTERY_LOW);
+        filter.addAction(Intent.ACTION_BATTERY_OKAY);
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
         filter.addAction(Intent.ACTION_ALARM_CHANGED);
         filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
@@ -601,13 +601,6 @@
         if (false) {
             Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
         }
-
-        if (mLowBatteryDialog != null
-            && mBatteryLevel >= BATTERY_LEVEL_CLOSE_WARNING
-            && SHOW_LOW_BATTERY_WARNING) {
-            mLowBatteryDialog.dismiss();
-            mBatteryShowLowOnEndCall = false;
-        }
     }
 
     private void onBatteryLow(Intent intent) {
@@ -626,6 +619,14 @@
         }
     }
 
+    private void onBatteryOkay(Intent intent) {
+        if (mLowBatteryDialog != null
+                && SHOW_LOW_BATTERY_WARNING) {
+            mLowBatteryDialog.dismiss();
+            mBatteryShowLowOnEndCall = false;
+        }
+    }
+
     private void showBatteryView() {    
         closeLastBatteryView();
         if (mLowBatteryDialog != null) {
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 48cbace..b44168a 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -119,6 +119,7 @@
         public void binderDied() {
             Log.i(TAG, "binder died for pkg=" + pkg);
             disable(0, token, pkg);
+            token.unlinkToDeath(this, 0);
         }
     }
 
@@ -494,6 +495,7 @@
             if (what == 0 || !token.isBinderAlive()) {
                 if (tok != null) {
                     mDisableRecords.remove(i);
+                    tok.token.unlinkToDeath(tok, 0);
                 }
             } else {
                 if (tok == null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bf5df88..c9dcd8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -667,7 +667,10 @@
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return CALL_STATE_IDLE;
-        }
+        } catch (NullPointerException ex) {
+          // the phone process is restarting.
+          return CALL_STATE_IDLE;
+      }
     }
 
     /** Data connection activity: No traffic. */
@@ -701,7 +704,10 @@
         } catch (RemoteException ex) {
             // the phone process is restarting.
             return DATA_ACTIVITY_NONE;
-        }
+        } catch (NullPointerException ex) {
+          // the phone process is restarting.
+          return DATA_ACTIVITY_NONE;
+      }
     }
 
     /** Data connection state: Disconnected. IP traffic not available. */
diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
index 2984fa8..3bbe0e1 100644
--- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
+++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java
@@ -187,22 +187,30 @@
     }
 
     /**
-     * Decode the "Extension-media" type for WSP pdu
-     *
-     * @param startIndex The starting position of the "Extension-media" in this pdu
-     *
-     * @return false when error(not a Extension-media) occur
-     *         return value can be retrieved by getValueString() method
-     *         length of data in pdu can be retrieved by getValue32() method
-     */
+    * Decode the "Extension-media" type for WSP PDU.
+    *
+    * @param startIndex The starting position of the "Extension-media" in this PDU.
+    *
+    * @return false on error, such as if there is no Extension-media at startIndex.
+    * Side-effects: updates stringValue (available with getValueString()), which will be
+    * null on error. The length of the data in the PDU is available with getValue32(), 0
+    * on error.
+    */
     public boolean decodeExtensionMedia(int startIndex) {
         int index = startIndex;
-        while (wspData[index] != 0) {
+        dataLength = 0;
+        stringValue = null;
+        int length = wspData.length;
+        boolean rtrn = index < length;
+
+        while (index < length && wspData[index] != 0) {
             index++;
         }
+
         dataLength  = index - startIndex + 1;
         stringValue = new String(wspData, startIndex, dataLength - 1);
-        return true;
+
+        return rtrn;
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 65d3362..3c8c10a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -94,7 +94,6 @@
     byte[] mEfCfis = null;
 
 
-    String spn;
     int spnDisplayCondition;
     // Numeric network codes listed in TS 51.011 EF[SPDI]
     ArrayList<String> spdiNetworks = null;
diff --git a/test-runner/android/test/MoreAsserts.java b/test-runner/android/test/MoreAsserts.java
index 2e74644..9e0d018 100644
--- a/test-runner/android/test/MoreAsserts.java
+++ b/test-runner/android/test/MoreAsserts.java
@@ -24,6 +24,7 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.ArrayList;
 import java.util.regex.MatchResult;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -316,8 +317,11 @@
      */
     public static void assertContentsInOrder(
             String message, Iterable<?> actual, Object... expected) {
-        Assert.assertEquals(message,
-                Arrays.asList(expected), Lists.newArrayList(actual));
+        ArrayList actualList = new ArrayList();
+        for (Object o : actual) {
+            actualList.add(o);
+        }
+        Assert.assertEquals(message, Arrays.asList(expected), actualList);
     }
 
     /**
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
index f623080..0991e8c 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/DatabaseGeneralTest.java
@@ -175,6 +175,7 @@
         assertEquals("+" + PHONE_NUMBER, number);
         c.close();
 
+        /*
         c = mDatabase.query("phones", null,
                 "PHONE_NUMBERS_EQUAL(num, '5551212')", null, null, null, null);
         assertNotNull(c);
@@ -183,6 +184,7 @@
         number = c.getString(c.getColumnIndexOrThrow("num"));
         assertEquals("+" + PHONE_NUMBER, number);
         c.close();
+        */
 
         c = mDatabase.query("phones", null,
                 "PHONE_NUMBERS_EQUAL(num, '011" + PHONE_NUMBER + "')", null, null, null, null);
@@ -203,85 +205,97 @@
         c.close();
     }
 
+
+    private void phoneNumberCompare(String phone1, String phone2, boolean equal)
+        throws Exception {
+        String[] temporalPhoneNumbers = new String[2];
+        temporalPhoneNumbers[0] = phone1;
+        temporalPhoneNumbers[1] = phone2;
+
+        Cursor cursor = mDatabase.rawQuery(
+                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
+                temporalPhoneNumbers);
+        try {
+            assertNotNull(cursor);
+            assertTrue(cursor.moveToFirst());
+            if (equal) {
+                assertEquals(String.format("Unexpectedly, \"%s != %s\".", phone1, phone2),
+                        "equal", cursor.getString(0));
+            } else {
+                assertEquals(String.format("Unexpectedly, \"%s\" == \"%s\".", phone1, phone2),
+                        "not equal", cursor.getString(0));
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception {
+        phoneNumberCompare(phone1, phone2, true);
+    }
+
+    private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception {
+        phoneNumberCompare(phone1, phone2, false);
+    }
+
     /**
      * Tests international matching issues for the PHONE_NUMBERS_EQUAL function.
      * 
      * @throws Exception
      */
+    @SmallTest
     public void testPhoneNumbersEqualInternationl() throws Exception {
-        Cursor c;
-        String[] phoneNumbers = new String[2];
+        assertPhoneNumberEqual("1", "1");
+        assertPhoneNumberEqual("123123", "123123");
+        assertPhoneNumberNotEqual("123123", "923123");
+        assertPhoneNumberNotEqual("123123", "123129");
+        assertPhoneNumberNotEqual("123123", "1231234");
+        assertPhoneNumberNotEqual("123123", "0123123");
+        assertPhoneNumberEqual("650-253-0000", "6502530000");
+        assertPhoneNumberEqual("650-253-0000", "650 253 0000");
+        assertPhoneNumberEqual("650 253 0000", "6502530000");
+        assertPhoneNumberEqual("+1 650-253-0000", "6502530000");
+        assertPhoneNumberEqual("001 650-253-0000", "6502530000");
+        assertPhoneNumberEqual("0111 650-253-0000", "6502530000");
 
         // Russian trunk digit
-        phoneNumbers[0] = "+79161234567"; // globablly dialable number
-        phoneNumbers[1] = "89161234567"; // in-country dialable number
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("+79161234567", "89161234567");
 
         // French trunk digit
-        phoneNumbers[0] = "+33123456789"; // globablly dialable number
-        phoneNumbers[1] = "0123456789"; // in-country dialable number
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
-
+        assertPhoneNumberEqual("+33123456789", "0123456789");
 
         // Trunk digit for city codes in the Netherlands
-        phoneNumbers[0] = "+31771234567"; // globablly dialable number
-        phoneNumbers[1] = "0771234567"; // in-country dialable number
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("+31771234567", "0771234567");
 
         // Test broken caller ID seen on call from Thailand to the US
-        phoneNumbers[0] = "+66811234567"; // in address book
-        phoneNumbers[1] = "166811234567"; // came in from the network
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("+66811234567", "166811234567");
 
         // Test the same in-country number with different country codes
-        phoneNumbers[0] = "+33123456789";
-        phoneNumbers[1] = "+1123456789";
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("not equal", c.getString(0));
-        c.close();
+        assertPhoneNumberNotEqual("+33123456789", "+1123456789");
 
         // Test one number with country code and the other without
-        phoneNumbers[0] = "5125551212";
-        phoneNumbers[1] = "+15125551212";
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("equal", c.getString(0));
-        c.close();
+        assertPhoneNumberEqual("5125551212", "+15125551212");
 
         // Test two NANP numbers that only differ in the area code
-        phoneNumbers[0] = "5125551212";
-        phoneNumbers[1] = "6505551212";
-        c = mDatabase.rawQuery(
-                "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?) THEN 'equal' ELSE 'not equal' END",
-                phoneNumbers);
-        assertTrue(c.moveToFirst());
-        assertEquals("not equal", c.getString(0));
-        c.close();
+        assertPhoneNumberNotEqual("5125551212", "6505551212");
+
+        // Japanese phone numbers
+        assertPhoneNumberEqual("090-1234-5678", "+819012345678");
+        assertPhoneNumberEqual("090(1234)5678", "+819012345678");
+        assertPhoneNumberEqual("090-1234-5678", "+81-90-1234-5678");
+
+        // Equador
+        assertPhoneNumberEqual("+593(800)123-1234", "8001231234");
+        assertPhoneNumberEqual("+593-2-1234-123", "21234123");
+
+        // Two continuous 0 at the beginning of the phone string should not be
+        // treated as trunk prefix.
+        assertPhoneNumberNotEqual("008001231234", "8001231234");
+
+        // Confirm that the bug found before does not re-appear.
+        assertPhoneNumberNotEqual("080-1234-5678", "+819012345678");
     }
 
     @MediumTest
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index c792e8e..cbcac6c 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -16,18 +16,15 @@
 
 package com.android.dumprendertree;
 
+import dalvik.system.VMRuntime;
+
 import android.app.Instrumentation;
 import android.content.Intent;
-
-import android.util.Log;
-
 import android.os.Bundle;
 import android.os.Debug;
-import android.os.Debug.MemoryInfo;
+import android.os.Process;
 import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.dumprendertree.TestShellActivity;
-import com.android.dumprendertree.TestShellCallback;
+import android.util.Log;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -70,9 +67,7 @@
         TestShellActivity activity = (TestShellActivity) getActivity();
 
         Log.v(LOGTAG, "About to run tests, calling gc first...");
-        Runtime.getRuntime().runFinalization();
-        Runtime.getRuntime().gc();
-        Runtime.getRuntime().gc();
+        freeMem();
 
         // Run tests
         runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis);
@@ -83,46 +78,73 @@
         activity.finish();
     }
 
+    private void freeMem() {
+        Log.v(LOGTAG, "freeMem: calling gc/finalization...");
+        final VMRuntime runtime = VMRuntime.getRuntime();
+
+        runtime.gcSoftReferences();
+        runtime.runFinalizationSync();
+        runtime.gcSoftReferences();
+        runtime.runFinalizationSync();
+        runtime.gcSoftReferences();
+        runtime.runFinalizationSync();
+        Runtime.getRuntime().runFinalization();
+        Runtime.getRuntime().gc();
+        Runtime.getRuntime().gc();
+
+    }
+
+    private void printRow(PrintStream ps, String format, Object...objs) {
+        ps.println(String.format(format, objs));
+    }
+
     private void dumpMemoryInfo() {
         try {
-            Log.v(LOGTAG, "About to dump meminfo, calling gc first...");
-            Runtime.getRuntime().runFinalization();
-            Runtime.getRuntime().gc();
-            Runtime.getRuntime().gc();
-
+            freeMem();
             Log.v(LOGTAG, "Dumping memory information.");
 
             FileOutputStream out = new FileOutputStream(LOAD_TEST_RESULT, true);
             PrintStream ps = new PrintStream(out);
 
-            MemoryInfo mi = new MemoryInfo();
-            Debug.getMemoryInfo(mi);
-
-            //try to fake the dumpsys format
-            //this will eventually be changed to XML
-            String format = "%15s:%9d%9d%9d%9d";
-            String pss =
-              String.format(format, "(Pss)",
-                  mi.nativePss, mi.dalvikPss, mi.otherPss,
-                  mi.nativePss + mi.dalvikPss + mi.otherPss);
-            String sd =
-              String.format(format, "(shared dirty)",
-                  mi.nativeSharedDirty, mi.dalvikSharedDirty, mi.otherSharedDirty,
-                  mi.nativeSharedDirty + mi.dalvikSharedDirty + mi.otherSharedDirty);
-            String pd =
-              String.format(format, "(priv dirty)",
-                  mi.nativePrivateDirty, mi.dalvikPrivateDirty, mi.otherPrivateDirty,
-                  mi.nativePrivateDirty + mi.dalvikPrivateDirty + mi.otherPrivateDirty);
-
             ps.print("\n\n\n");
-            ps.println("** MEMINFO in pid 0 [com.android.dumprendertree] **");
-            ps.println("                   native   dalvik    other    total");
-            ps.println("           size:    12060     5255      N/A    17315");
-            ps.println("      allocated:    12060     5255      N/A    17315");
-            ps.println("           free:    12060     5255      N/A    17315");
-            ps.println(pss);
-            ps.println(sd);
-            ps.println(pd);
+            ps.println("** MEMINFO in pid " + Process.myPid()
+                    + " [com.android.dumprendertree] **");
+            String formatString = "%17s %8s %8s %8s %8s";
+
+            long nativeMax = Debug.getNativeHeapSize() / 1024;
+            long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
+            long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
+            Runtime runtime = Runtime.getRuntime();
+            long dalvikMax = runtime.totalMemory() / 1024;
+            long dalvikFree = runtime.freeMemory() / 1024;
+            long dalvikAllocated = dalvikMax - dalvikFree;
+
+
+            Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
+            Debug.getMemoryInfo(memInfo);
+
+            final int nativeShared = memInfo.nativeSharedDirty;
+            final int dalvikShared = memInfo.dalvikSharedDirty;
+            final int otherShared = memInfo.otherSharedDirty;
+
+            final int nativePrivate = memInfo.nativePrivateDirty;
+            final int dalvikPrivate = memInfo.dalvikPrivateDirty;
+            final int otherPrivate = memInfo.otherPrivateDirty;
+
+            printRow(ps, formatString, "", "native", "dalvik", "other", "total");
+            printRow(ps, formatString, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
+            printRow(ps, formatString, "allocated:", nativeAllocated, dalvikAllocated, "N/A",
+                    nativeAllocated + dalvikAllocated);
+            printRow(ps, formatString, "free:", nativeFree, dalvikFree, "N/A",
+                    nativeFree + dalvikFree);
+
+            printRow(ps, formatString, "(Pss):", memInfo.nativePss, memInfo.dalvikPss,
+                    memInfo.otherPss, memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss);
+
+            printRow(ps, formatString, "(shared dirty):", nativeShared, dalvikShared, otherShared,
+                    nativeShared + dalvikShared + otherShared);
+            printRow(ps, formatString, "(priv dirty):", nativePrivate, dalvikPrivate, otherPrivate,
+                    nativePrivate + dalvikPrivate + otherPrivate);
             ps.print("\n\n\n");
             ps.flush();
             ps.close();
@@ -142,7 +164,7 @@
                     LoadTestsAutoTest.this.notifyAll();
                 }
             }
-            
+
             public void timedOut(String url) {
             }
         });
diff --git a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java
index 719e758..aebd68c 100644
--- a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java
@@ -46,7 +46,7 @@
      */
     public void testVibrate() throws RemoteException {
         try {
-            mHardwareService.vibrate(2000);
+            mHardwareService.vibrate(2000, new Binder());
             fail("vibrate did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
@@ -77,7 +77,7 @@
      */
     public void testCancelVibrate() throws RemoteException {
         try {
-            mHardwareService.cancelVibrate();
+            mHardwareService.cancelVibrate(new Binder());
             fail("cancelVibrate did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c4dff6a..79b8fc4 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -253,6 +253,15 @@
     IWifiManager mService;
     Handler mHandler;
 
+    /* Maximum number of active locks we allow.
+     * This limit was added to prevent apps from creating a ridiculous number
+     * of locks and crashing the system by overflowing the global ref table.
+     */
+    private static final int MAX_ACTIVE_LOCKS = 50;
+
+    /* Number of currently active WifiLocks and MulticastLocks */
+    private int mActiveLockCount;
+
     /**
      * Create a new WifiManager instance.
      * Applications will almost always want to use
@@ -703,6 +712,14 @@
                 if (mRefCounted ? (++mRefCount > 0) : (!mHeld)) {
                     try {
                         mService.acquireWifiLock(mBinder, mLockType, mTag);
+                        synchronized (WifiManager.this) {
+                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
+                                mService.releaseWifiLock(mBinder);
+                                throw new UnsupportedOperationException(
+                                            "Exceeded maximum number of wifi locks");
+                            }
+                            mActiveLockCount++;
+                        }
                     } catch (RemoteException ignore) {
                     }
                     mHeld = true;
@@ -727,6 +744,9 @@
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
                         mService.releaseWifiLock(mBinder);
+                        synchronized (WifiManager.this) {
+                            mActiveLockCount--;
+                        }
                     } catch (RemoteException ignore) {
                     }
                     mHeld = false;
@@ -784,6 +804,9 @@
                 if (mHeld) {
                     try {
                         mService.releaseWifiLock(mBinder);
+                        synchronized (WifiManager.this) {
+                            mActiveLockCount--;
+                        }
                     } catch (RemoteException ignore) {
                     }
                 }
@@ -878,6 +901,14 @@
                 if (!mHeld) {
                     try {
                         mService.acquireMulticastLock(mBinder, mTag);
+                        synchronized (WifiManager.this) {
+                            if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
+                                mService.releaseMulticastLock();
+                                throw new UnsupportedOperationException(
+                                        "Exceeded maximum number of wifi locks");
+                            }
+                            mActiveLockCount++;
+                        }
                         mHeld = true;
                     } catch (RemoteException ignore) {
                     }
@@ -902,6 +933,9 @@
                 if (mHeld) {
                     try {
                         mService.releaseMulticastLock();
+                        synchronized (WifiManager.this) {
+                            mActiveLockCount--;
+                        }
                         mHeld = false;
                     } catch (RemoteException ignore) {
                     }