Merge change 5979 into donut

* changes:
  Remove the constraint to assign new uid when code path changes for system packages
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/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/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index 1f11762..d4933ac 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -32,10 +32,24 @@
     /**
      * Tell the system service that the caller has made changes to its
      * data, and therefore needs to undergo an incremental backup pass.
+     *
+     * Any application can invoke this method for its own package, but
+     * only callers who hold the android.permission.BACKUP permission
+     * may invoke it for arbitrary packages.
      */
     void dataChanged(String packageName);
 
     /**
+     * Erase all backed-up data for the given package from the storage
+     * destination.
+     *
+     * Any application can invoke this method for its own package, but
+     * only callers who hold the android.permission.BACKUP permission
+     * may invoke it for arbitrary packages.
+     */
+    void clearBackupData(String packageName);
+
+    /**
      * Notifies the Backup Manager Service that an agent has become available.  This
      * method is only invoked by the Activity Manager.
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6b723bc..263f927 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).
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 3ce951f..091bc17 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -371,7 +371,7 @@
     }
     private native final void native_takePicture();
 
-    // These match the enum in libs/android_runtime/android_hardware_Camera.cpp
+    // These match the enum in include/ui/Camera.h
     /** Unspecified camerar error.  @see #ErrorCallback */
     public static final int CAMERA_ERROR_UNKNOWN = 1;
     /** Media server died. In this case, the application must release the
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2cca837..7017333 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1337,6 +1337,49 @@
          */
         public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
 
+        /**
+         * Settings to backup. This is here so that it's in the same place as the settings
+         * keys and easy to update.
+         * @hide
+         */
+        public static final String[] SETTINGS_TO_BACKUP = {
+            STAY_ON_WHILE_PLUGGED_IN,
+            END_BUTTON_BEHAVIOR,
+            WIFI_SLEEP_POLICY,
+            WIFI_USE_STATIC_IP,
+            WIFI_STATIC_IP,
+            WIFI_STATIC_GATEWAY,
+            WIFI_STATIC_NETMASK,
+            WIFI_STATIC_DNS1,
+            WIFI_STATIC_DNS2,
+            BLUETOOTH_DISCOVERABILITY,
+            BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+            DIM_SCREEN,
+            SCREEN_OFF_TIMEOUT,
+            SCREEN_BRIGHTNESS,
+            VIBRATE_ON,
+            NOTIFICATIONS_USE_RING_VOLUME,
+            RINGTONE,
+            NOTIFICATION_SOUND,
+            TEXT_AUTO_REPLACE,
+            TEXT_AUTO_CAPS,
+            TEXT_AUTO_PUNCTUATE,
+            TEXT_SHOW_PASSWORD,
+            AUTO_TIME,
+            TIME_12_24,
+            DATE_FORMAT,
+            ACCELEROMETER_ROTATION,
+            DTMF_TONE_WHEN_DIALING,
+            DTMF_TONE_TYPE_WHEN_DIALING,
+            EMERGENCY_TONE,
+            CALL_AUTO_RETRY,
+            HEARING_AID,
+            TTY_MODE,
+            SOUND_EFFECTS_ENABLED,
+            HAPTIC_FEEDBACK_ENABLED,
+            SHOW_WEB_SUGGESTIONS
+        };
+
         // Settings moved to Settings.Secure
 
         /**
@@ -2226,6 +2269,54 @@
         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";
+
+        /**
+         * @hide
+         */
+        public static final String[] SETTINGS_TO_BACKUP = {
+            INSTALL_NON_MARKET_APPS,
+            PARENTAL_CONTROL_ENABLED,
+            PARENTAL_CONTROL_REDIRECT_URL,
+            USB_MASS_STORAGE_ENABLED,
+            ACCESSIBILITY_ENABLED,
+            ENABLED_ACCESSIBILITY_SERVICES,
+            TTS_USE_DEFAULTS,
+            TTS_DEFAULT_RATE,
+            TTS_DEFAULT_PITCH,
+            TTS_DEFAULT_SYNTH,
+            TTS_DEFAULT_LANG,
+            TTS_DEFAULT_COUNTRY,
+            WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+            WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+            WIFI_NUM_ALLOWED_CHANNELS,
+            WIFI_NUM_OPEN_NETWORKS_KEPT,
+            BACKGROUND_DATA,
+            PREFERRED_NETWORK_MODE,
+            PREFERRED_TTY_MODE,
+            CDMA_CELL_BROADCAST_SMS,
+            PREFERRED_CDMA_SUBSCRIPTION,
+            ENHANCED_VOICE_PRIVACY_ENABLED
+        };
+
+        /**
          * Helper method for determining if a location provider is enabled.
          * @param cr the content resolver to use
          * @param provider the location provider to query
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index b245713..714429d 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -118,7 +118,12 @@
         public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
         public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
         public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
-        
+
+        // return codes for a TTS engine's check data activity
+        public static final String VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+        public static final String VOICE_DATA_FILES = "dataFiles";
+        public static final String VOICE_DATA_FILES_INFO = "dataFilesInfo";
+
         // keys for the parameters passed with speak commands
         public static final String TTS_KEY_PARAM_RATE = "rate";
         public static final String TTS_KEY_PARAM_LANGUAGE = "language";
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/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 4bef265..af06965 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -83,13 +83,25 @@
     boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);
 
     /**
-     * Finish sending application data to the backup destination.  This must be
-     * called after {@link #performBackup} to ensure that all data is sent.  Only
-     * when this method returns true can the backup be assumed to have succeeded.
+     * Erase the give application's data from the backup destination.  This clears
+     * out the given package's data from the current backup set, making it as though
+     * the app had never yet been backed up.  After this is called, {@link finishBackup}
+     * must be called to ensure that the operation is recorded successfully.
      *
      * @return false if errors occurred (the backup should be aborted and rescheduled),
      *   true if everything is OK so far (but {@link #finishBackup} must be called).
      */
+    boolean clearBackupData(in PackageInfo packageInfo);
+
+    /**
+     * Finish sending application data to the backup destination.  This must be
+     * called after {@link #performBackup} or {@link clearBackupData} to ensure that
+     * all data is sent.  Only when this method returns true can a backup be assumed
+     * to have succeeded.
+     *
+     * @return false if errors occurred (the backup should be aborted and rescheduled),
+     *   true if everything is OK.
+     */
     boolean finishBackup();
 
     /**
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index c5d9d403..2facce2 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -111,6 +111,17 @@
         }
     }
 
+    public boolean clearBackupData(PackageInfo packageInfo) {
+        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
+
+        File packageDir = new File(mDataDir, packageInfo.packageName);
+        for (File f : packageDir.listFiles()) {
+            f.delete();
+        }
+        packageDir.delete();
+        return true;
+    }
+
     public boolean finishBackup() throws RemoteException {
         if (DEBUG) Log.v(TAG, "finishBackup()");
         return true;
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 3dcc09f..77a8a72 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -38,12 +38,6 @@
     kErrorCallback = 5
 };
 
-enum CameraError {
-    kCameraErrorUnknown = 1,
-    kCameraErrorMediaServer = 100
-};
-
-
 struct fields_t {
     jfieldID    context;
     jfieldID    surface;
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/include/ui/Camera.h b/include/ui/Camera.h
index 97e0e90..e3544ab 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -76,6 +76,12 @@
     CAMERA_MSG_COMPRESSED_IMAGE
 };
 
+// camera fatal errors
+enum {
+    CAMERA_ERROR_UKNOWN  = 1,
+    CAMERA_ERROR_SERVER_DIED = 100
+};
+
 class ICameraService;
 class ICamera;
 class Surface;
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 324111b..75ca22c 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);
@@ -893,7 +894,7 @@
             }
             LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount);
         } else {
-            LOGE("mA2dpDisableCount is already zero");
+            LOGV("mA2dpDisableCount is already zero");
         }
     }
 }
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index a481ce7..975594f 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -312,7 +312,7 @@
 
 void Camera::binderDied(const wp<IBinder>& who) {
     LOGW("ICamera died");
-    notifyCallback(CAMERA_MSG_ERROR, DEAD_OBJECT, 0);
+    notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0);
 }
 
 void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) {
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 2407d87..9109606 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -4,6 +4,7 @@
 
     <application android:allowClearUserData="false"
                  android:label="@string/app_label"
+                 android:backupAgent="SettingsBackupAgent"
                  android:icon="@drawable/ic_launcher_settings">
 
         <provider android:name="SettingsProvider" android:authorities="settings"
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index c283418..f8adaa1 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -41,4 +41,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 660b469..6dd1175 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 = 34;
+    private static final int DATABASE_VERSION = 35;
 
     private Context mContext;
 
@@ -386,6 +386,20 @@
             upgradeVersion = 34;
         }
 
+        if (upgradeVersion == 34) {
+            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 = 35;
+        }
+        
         if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
@@ -690,9 +704,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/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
new file mode 100644
index 0000000..1736a491
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import android.backup.BackupDataInput;
+import android.backup.BackupDataOutput;
+import android.backup.BackupHelperAgent;
+import android.bluetooth.BluetoothDevice;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Performs backup and restore of the System and Secure settings.
+ * List of settings that are backed up are stored in the Settings.java file
+ */
+public class SettingsBackupAgent extends BackupHelperAgent {
+
+    private static final String KEY_SYSTEM = "system";
+    private static final String KEY_SECURE = "secure";
+    private static final String KEY_SYNC = "sync_providers";
+
+    private static String[] sortedSystemKeys = null;
+    private static String[] sortedSecureKeys = null;
+
+    private static final byte[] EMPTY_DATA = new byte[0];
+
+    private static final String TAG = "SettingsBackupAgent";
+
+    private static final int COLUMN_ID = 0;
+    private static final int COLUMN_NAME = 1;
+    private static final int COLUMN_VALUE = 2;
+
+    private static final String[] PROJECTION = {
+        Settings.NameValueTable._ID,
+        Settings.NameValueTable.NAME,
+        Settings.NameValueTable.VALUE
+    };
+
+    private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
+    private static final String FILE_BT_ROOT = "/data/misc/hcid/";
+
+    private SettingsHelper mSettingsHelper;
+
+    public void onCreate() {
+        mSettingsHelper = new SettingsHelper(this);
+        super.onCreate();
+    }
+
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) throws IOException {
+
+        byte[] systemSettingsData = getSystemSettings();
+        byte[] secureSettingsData = getSecureSettings();
+        byte[] syncProviders = mSettingsHelper.getSyncProviders();
+        
+        data.writeEntityHeader(KEY_SYSTEM, systemSettingsData.length);
+        data.writeEntityData(systemSettingsData, systemSettingsData.length);
+
+        data.writeEntityHeader(KEY_SECURE, secureSettingsData.length);
+        data.writeEntityData(secureSettingsData, secureSettingsData.length);
+
+        data.writeEntityHeader(KEY_SYNC, syncProviders.length);
+        data.writeEntityData(syncProviders, syncProviders.length);
+        
+        //TODO: Permissions problem : backupFile(FILE_WIFI_SUPPLICANT, data);
+    }
+
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode,
+            ParcelFileDescriptor newState) throws IOException {
+
+        enableWifi(false);
+        enableBluetooth(false);
+
+        while (data.readNextHeader()) {
+            final String key = data.getKey();
+            if (KEY_SYSTEM.equals(key)) {
+                restoreSettings(data, Settings.System.CONTENT_URI);
+            } else if (KEY_SECURE.equals(key)) {
+                restoreSettings(data, Settings.Secure.CONTENT_URI);
+            } else if (FILE_WIFI_SUPPLICANT.equals(key)) {
+                restoreFile(FILE_WIFI_SUPPLICANT, data);
+            } else if (KEY_SYNC.equals(key)) {
+                mSettingsHelper.setSyncProviders(data);
+            } else {
+                data.skipEntityData();
+            }
+        }
+    }
+
+    private byte[] getSystemSettings() {
+        Cursor sortedCursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION,
+                null, null, Settings.NameValueTable.NAME);
+        // Copy and sort the array
+        if (sortedSystemKeys == null) {
+            sortedSystemKeys = copyAndSort(Settings.System.SETTINGS_TO_BACKUP);
+        }
+        byte[] result = extractRelevantValues(sortedCursor, sortedSystemKeys);
+        sortedCursor.close();
+        return result;
+    }
+
+    private byte[] getSecureSettings() {
+        Cursor sortedCursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION,
+                null, null, Settings.NameValueTable.NAME);
+        // Copy and sort the array
+        if (sortedSecureKeys == null) {
+            sortedSecureKeys = copyAndSort(Settings.Secure.SETTINGS_TO_BACKUP);
+        }
+        byte[] result = extractRelevantValues(sortedCursor, sortedSecureKeys);
+        sortedCursor.close();
+        return result;
+    }
+
+    private void restoreSettings(BackupDataInput data, Uri contentUri) {
+        ContentValues cv = new ContentValues(2);
+        byte[] settings = new byte[data.getDataSize()];
+        try {
+            data.readEntityData(settings, 0, settings.length);
+        } catch (IOException ioe) {
+            Log.e(TAG, "Couldn't read entity data");
+            return;
+        }
+        int pos = 0;
+        while (pos < settings.length) {
+            int length = readInt(settings, pos);
+            pos += 4;
+            String settingName = length > 0? new String(settings, pos, length) : null;
+            pos += length;
+            length = readInt(settings, pos);
+            pos += 4;
+            String settingValue = length > 0? new String(settings, pos, length) : null;
+            pos += length;
+            if (!TextUtils.isEmpty(settingName) && !TextUtils.isEmpty(settingValue)) {
+                //Log.i(TAG, "Restore " + settingName + " = " + settingValue);
+                cv.clear();
+                cv.put(Settings.NameValueTable.NAME, settingName);
+                cv.put(Settings.NameValueTable.VALUE, settingValue);
+                getContentResolver().insert(contentUri, cv);
+                mSettingsHelper.restoreValue(settingName, settingValue);
+            }
+        }
+    }
+
+    private String[] copyAndSort(String[] keys) {
+        String[] sortedKeys = new String[keys.length];
+        System.arraycopy(keys, 0, sortedKeys, 0, keys.length);
+        Arrays.sort(sortedKeys);
+        return sortedKeys;
+    }
+
+    /**
+     * Given a cursor sorted by key name and a set of keys sorted by name, 
+     * extract the required keys and values and write them to a byte array.
+     * @param sortedCursor
+     * @param sortedKeys
+     * @return
+     */
+    byte[] extractRelevantValues(Cursor sortedCursor, String[] sortedKeys) {
+        byte[][] values = new byte[sortedKeys.length * 2][]; // keys and values
+        if (!sortedCursor.moveToFirst()) {
+            Log.e(TAG, "Couldn't read from the cursor");
+            return new byte[0];
+        }
+        int keyIndex = 0;
+        int totalSize = 0;
+        while (!sortedCursor.isAfterLast()) {
+            String name = sortedCursor.getString(COLUMN_NAME);
+            while (sortedKeys[keyIndex].compareTo(name.toString()) < 0) {
+                keyIndex++;
+                if (keyIndex == sortedKeys.length) break;
+            }
+            if (keyIndex < sortedKeys.length && name.equals(sortedKeys[keyIndex])) {
+                String value = sortedCursor.getString(COLUMN_VALUE);
+                byte[] nameBytes = name.toString().getBytes();
+                totalSize += 4 + nameBytes.length;
+                values[keyIndex * 2] = nameBytes;
+                byte[] valueBytes;
+                if (TextUtils.isEmpty(value)) {
+                    valueBytes = null;
+                    totalSize += 4;
+                } else {
+                    valueBytes = value.toString().getBytes();
+                    totalSize += 4 + valueBytes.length;
+                    //Log.i(TAG, "Backing up " + name + " = " + value);
+                }
+                values[keyIndex * 2 + 1] = valueBytes;
+                keyIndex++;
+            }
+            if (keyIndex == sortedKeys.length || !sortedCursor.moveToNext()) {
+                break;
+            }
+        }
+
+        byte[] result = new byte[totalSize];
+        int pos = 0;
+        for (int i = 0; i < sortedKeys.length * 2; i++) {
+            if (values[i] != null) {
+                pos = writeInt(result, pos, values[i].length);
+                pos = writeBytes(result, pos, values[i]);
+            }
+        }
+        return result;
+    }
+
+    private void backupFile(String filename, BackupDataOutput data) {
+        try {
+            File file = new File(filename);
+            if (file.exists()) {
+                byte[] bytes = new byte[(int) file.length()];
+                FileInputStream fis = new FileInputStream(file);
+                int offset = 0;
+                int got = 0;
+                do {
+                    got = fis.read(bytes, offset, bytes.length - offset);
+                    if (got > 0) offset += got;
+                } while (offset < bytes.length && got > 0);
+                data.writeEntityHeader(filename, bytes.length);
+                data.writeEntityData(bytes, bytes.length);
+            } else {
+                data.writeEntityHeader(filename, 0);
+                data.writeEntityData(EMPTY_DATA, 0);
+            }
+        } catch (IOException ioe) {
+            Log.w(TAG, "Couldn't backup " + filename);
+        }
+    }
+
+    private void restoreFile(String filename, BackupDataInput data) {
+        byte[] bytes = new byte[data.getDataSize()];
+        if (bytes.length <= 0) return;
+        try {
+            data.readEntityData(bytes, 0, bytes.length);
+            FileOutputStream fos = new FileOutputStream(filename);
+            fos.write(bytes);
+        } catch (IOException ioe) {
+            Log.w(TAG, "Couldn't restore " + filename);
+        }
+    }
+
+    /**
+     * Write an int in BigEndian into the byte array.
+     * @param out byte array
+     * @param pos current pos in array
+     * @param value integer to write
+     * @return the index after adding the size of an int (4)
+     */
+    private int writeInt(byte[] out, int pos, int value) {
+        out[pos + 0] = (byte) ((value >> 24) & 0xFF);
+        out[pos + 1] = (byte) ((value >> 16) & 0xFF);
+        out[pos + 2] = (byte) ((value >>  8) & 0xFF);
+        out[pos + 3] = (byte) ((value >>  0) & 0xFF);
+        return pos + 4;
+    }
+
+    private int writeBytes(byte[] out, int pos, byte[] value) {
+        System.arraycopy(value, 0, out, pos, value.length);
+        return pos + value.length;
+    }
+
+    private int readInt(byte[] in, int pos) {
+        int result =
+                ((in[pos    ] & 0xFF) << 24) |
+                ((in[pos + 1] & 0xFF) << 16) |
+                ((in[pos + 2] & 0xFF) <<  8) |
+                ((in[pos + 3] & 0xFF) <<  0);
+        return result;
+    }
+
+    private void enableWifi(boolean enable) {
+        WifiManager wfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        if (wfm != null) {
+            wfm.setWifiEnabled(enable);
+        }
+    }
+
+    private void enableBluetooth(boolean enable) {
+        BluetoothDevice bt = (BluetoothDevice) getSystemService(Context.BLUETOOTH_SERVICE);
+        if (bt != null) {
+            if (!enable) {
+                bt.disable();
+            } else {
+                bt.enable();
+            }
+        }
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
new file mode 100644
index 0000000..b065587
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.backup.BackupDataInput;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.IHardwareService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.content.IContentService;
+import android.util.Log;
+
+public class SettingsHelper {
+    private static final String TAG = "SettingsHelper";
+
+    private Context mContext;
+    private AudioManager mAudioManager;
+    private IContentService mContentService;
+    private static final String SYNC_AUTO = "auto_sync";
+    private static final String SYNC_MAIL = "gmail-ls_sync";
+    private static final String SYNC_CALENDAR = "calendar_sync";
+    private static final String SYNC_CONTACTS = "contacts_sync";
+
+    public SettingsHelper(Context context) {
+        mContext = context;
+        mAudioManager = (AudioManager) context
+                .getSystemService(Context.AUDIO_SERVICE);
+        mContentService = ContentResolver.getContentService();
+    }
+
+    public void restoreValue(String name, String value) {
+        if (Settings.System.SCREEN_BRIGHTNESS.equals(name)) {
+            setBrightness(Integer.parseInt(value));
+        } else if (Settings.System.SOUND_EFFECTS_ENABLED.equals(name)) {
+            if (Integer.parseInt(value) == 1) {
+                mAudioManager.loadSoundEffects();
+            } else {
+                mAudioManager.unloadSoundEffects();
+            }
+        }
+    }
+
+    private void setBrightness(int brightness) {
+        try {
+            IHardwareService hardware = IHardwareService.Stub
+                    .asInterface(ServiceManager.getService("hardware"));
+            if (hardware != null) {
+                hardware.setBacklights(brightness);
+            }
+        } catch (RemoteException doe) {
+
+        }
+    }
+
+    static final String[] PROVIDERS = { "gmail-ls", "calendar", "contacts" };
+    
+    byte[] getSyncProviders() {
+        byte[] sync = new byte[1 + PROVIDERS.length];
+        try {
+            sync[0] = (byte) (mContentService.getListenForNetworkTickles() ? 1 : 0);
+            for (int i = 0; i < PROVIDERS.length; i++) {
+                sync[i + 1] = (byte) 
+                        (mContentService.getSyncProviderAutomatically(PROVIDERS[i]) ? 1 : 0);
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Unable to backup sync providers");
+            return sync;
+        }
+        return sync;
+    }
+    
+    void setSyncProviders(BackupDataInput backup) {
+        byte[] sync = new byte[backup.getDataSize()];
+
+        try {
+            backup.readEntityData(sync, 0, sync.length);
+            mContentService.setListenForNetworkTickles(sync[0] == 1);
+            for (int i = 0; i < PROVIDERS.length; i++) {
+                mContentService.setSyncProviderAutomatically(PROVIDERS[i], sync[i + 1] > 0);
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Unable to restore sync providers");
+        } catch (java.io.IOException ioe) {
+            Log.w(TAG, "Unable to read sync settings");
+        }
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3db52eb..a21bf32 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -16,6 +16,9 @@
 
 package com.android.providers.settings;
 
+import java.io.FileNotFoundException;
+
+import android.backup.IBackupManager;
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -27,6 +30,7 @@
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.DrmStore;
 import android.provider.MediaStore;
@@ -34,8 +38,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.io.FileNotFoundException;
-
 public class SettingsProvider extends ContentProvider {
     private static final String TAG = "SettingsProvider";
     private static final boolean LOCAL_LOGV = false;
@@ -137,6 +139,17 @@
             SystemProperties.set(property, Long.toString(version));
         }
 
+        // Inform the backup manager about a data change
+        IBackupManager ibm = IBackupManager.Stub.asInterface(
+                ServiceManager.getService(Context.BACKUP_SERVICE));
+        if (ibm != null) {
+            try {
+                ibm.dataChanged(getContext().getPackageName());
+            } catch (Exception e) {
+                // Try again later
+            }
+        }
+
         // Now send the notification through the content framework.
 
         String notify = uri.getQueryParameter("notify");
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index bd8b8ef..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;
@@ -57,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;
@@ -80,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
         
@@ -91,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
         
@@ -108,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) {
@@ -175,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('/');
@@ -385,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) {
@@ -620,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);
@@ -780,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);
         }
     }
@@ -799,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);
     }
 
@@ -873,8 +877,10 @@
                     stream.close();
                 }
             } catch (IOException ex) {
+                // Ignore
             }
             if (file.exists()) {
+                //noinspection ResultOfMethodCallIgnored
                 file.delete();
             }
         }
@@ -892,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) {
@@ -993,6 +999,7 @@
                 stream.close();
             }
         } catch (IOException e) {
+            // Ignore
         }
 
         if (success) {
@@ -1088,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);
@@ -1110,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/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index b15c06b..c3b9157 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -74,10 +74,6 @@
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
 
-    // Secure settings
-    private static final String BACKUP_TRANSPORT_SETTING = "backup_transport";
-    private static final String BACKUP_ENABLED_SETTING = "backup_enabled";
-
     // How often we perform a backup pass.  Privileged external callers can
     // trigger an immediate pass.
     private static final long BACKUP_INTERVAL = 60 * 60 * 1000;
@@ -85,6 +81,7 @@
     private static final int MSG_RUN_BACKUP = 1;
     private static final int MSG_RUN_FULL_BACKUP = 2;
     private static final int MSG_RUN_RESTORE = 3;
+    private static final int MSG_RUN_CLEAR = 4;
 
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -152,6 +149,16 @@
         }
     }
 
+    private class ClearParams {
+        public IBackupTransport transport;
+        public PackageInfo packageInfo;
+
+        ClearParams(IBackupTransport _transport, PackageInfo _info) {
+            transport = _transport;
+            packageInfo = _info;
+        }
+    }
+
     // Where we keep our journal files and other bookkeeping
     private File mBaseStateDir;
     private File mDataDir;
@@ -165,10 +172,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 = (Settings.Secure.getInt(mContext.getContentResolver(),
-                BACKUP_ENABLED_SETTING, 1) != 0);
+        mEnabled = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.BACKUP_ENABLED, 0) != 0;
         mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
         mDataDir = Environment.getDownloadCacheDirectory();
 
@@ -192,13 +197,10 @@
         registerTransport(localName.flattenToShortString(), mLocalTransport);
 
         mGoogleTransport = null;
-        // !!! TODO: set up the default transport name "the right way"
-        mCurrentTransport = Settings.Secure.getString(mContext.getContentResolver(),
-                BACKUP_TRANSPORT_SETTING);
-        if (mCurrentTransport == null) {
-            mCurrentTransport = "com.google.android.backup/.BackupTransportService";
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    BACKUP_TRANSPORT_SETTING, mCurrentTransport);
+        mCurrentTransport = Settings.Secure.getString(context.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT);
+        if ("".equals(mCurrentTransport)) {
+            mCurrentTransport = null;
         }
         if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport);
 
@@ -391,9 +393,17 @@
             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;
             }
+
+            case MSG_RUN_CLEAR:
+            {
+                ClearParams params = (ClearParams)msg.obj;
+                (new PerformClearThread(params.transport, params.packageInfo)).start();
+                break;
+            }
             }
         }
     }
@@ -837,6 +847,7 @@
         PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
                 long restoreSetToken) {
             mTransport = transport;
+            Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
             mObserver = observer;
             mToken = restoreSetToken;
 
@@ -850,7 +861,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:
              *
@@ -1011,6 +1023,8 @@
                     Log.e(TAG, "Error finishing restore", e);
                 }
 
+                Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
                 if (mObserver != null) {
                     try {
                         mObserver.restoreFinished(error);
@@ -1026,6 +1040,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();
@@ -1073,6 +1089,37 @@
         }
     }
 
+    class PerformClearThread extends Thread {
+        IBackupTransport mTransport;
+        PackageInfo mPackage;
+
+        PerformClearThread(IBackupTransport transport, PackageInfo packageInfo) {
+            mTransport = transport;
+            mPackage = packageInfo;
+        }
+
+        @Override
+        public void run() {
+            try {
+                // Clear the on-device backup state to ensure a full backup next time
+                File stateDir = new File(mBaseStateDir, mTransport.transportDirName());
+                File stateFile = new File(stateDir, mPackage.packageName);
+                stateFile.delete();
+
+                // Tell the transport to remove all the persistent storage for the app
+                mTransport.clearBackupData(mPackage);
+            } catch (RemoteException e) {
+                // can't happen; the transport is local
+            } finally {
+                try {
+                    mTransport.finishBackup();
+                } catch (RemoteException e) {
+                    // can't happen; the transport is local
+                }
+            }
+        }
+    }
+
 
     // ----- IBackupManager binder interface -----
 
@@ -1086,7 +1133,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 {
@@ -1144,10 +1191,56 @@
         }
     }
 
+    // Clear the given package's backup data from the current transport
+    public void clearBackupData(String packageName) {
+        if (DEBUG) Log.v(TAG, "clearBackupData() of " + packageName);
+        PackageInfo info;
+        try {
+            info = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+            return;
+        }
+
+        // If the caller does not hold the BACKUP permission, it can only request a
+        // wipe of its own backed-up data.
+        HashSet<ApplicationInfo> apps;
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            apps = mBackupParticipants.get(Binder.getCallingUid());
+        } else {
+            // a caller with full permission can ask to back up any participating app
+            // !!! TODO: allow data-clear of ANY app?
+            if (DEBUG) Log.v(TAG, "Privileged caller, allowing clear of other apps");
+            apps = new HashSet<ApplicationInfo>();
+            int N = mBackupParticipants.size();
+            for (int i = 0; i < N; i++) {
+                HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
+                if (s != null) {
+                    apps.addAll(s);
+                }
+            }
+        }
+
+        // now find the given package in the set of candidate apps
+        for (ApplicationInfo app : apps) {
+            if (app.packageName.equals(packageName)) {
+                if (DEBUG) Log.v(TAG, "Found the app - running clear process");
+                // found it; fire off the clear request
+                synchronized (mQueueLock) {
+                    Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
+                            new ClearParams(getTransport(mCurrentTransport), info));
+                    mBackupHandler.sendMessage(msg);
+                }
+                break;
+            }
+        }
+    }
+
     // 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) {
@@ -1157,12 +1250,12 @@
 
     // 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) {
-            Settings.Secure.putInt(mContext.getContentResolver(), BACKUP_ENABLED_SETTING,
-                    enable ? 1 : 0);
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    Settings.Secure.BACKUP_ENABLED, enable ? 1 : 0);
             mEnabled = enable;
         }
 
@@ -1179,20 +1272,21 @@
 
     // 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);
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "getCurrentTransport");
+        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>();
@@ -1213,15 +1307,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;
-                Settings.Secure.putString(mContext.getContentResolver(), BACKUP_TRANSPORT_SETTING,
-                        transport);
+                Settings.Secure.putString(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_TRANSPORT, transport);
                 Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport
                         + " returning " + prevTransport);
             } else {
@@ -1267,7 +1361,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) {
@@ -1293,7 +1387,7 @@
 
         // --- Binder interface ---
         public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
-            mContext.enforceCallingPermission("android.permission.BACKUP",
+            mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
                     "getAvailableRestoreSets");
 
             try {
@@ -1312,7 +1406,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++) {
@@ -1330,9 +1426,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) {
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/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index cfa625c..2dd70ef 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -2325,6 +2325,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;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f716571..2fe4dd4 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -745,6 +745,8 @@
 
     int mFactoryTest;
 
+    boolean mCheckedForSetup;
+    
     /**
      * The time at which we will allow normal application switches again,
      * after a call to {@link #stopAppSwitches()}.
@@ -1774,6 +1776,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;
     }
 
@@ -2355,6 +2363,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.
      *
@@ -2376,37 +2474,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;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index b886410..e25de81 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -92,7 +92,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/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/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java b/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java
index 51e841c..7720041 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/TextUtilsTest.java
@@ -276,6 +276,7 @@
         Spannable s3 = new SpannableString(s1);
         s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
         TextPaint p = new TextPaint();
+        p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
 
         for (int i = 0; i < 100; i++) {
             for (int j = 0; j < 3; j++) {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5b8ced6..7a15f27 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
@@ -702,6 +711,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;
@@ -726,6 +743,9 @@
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
                         mService.releaseWifiLock(mBinder);
+                        synchronized (WifiManager.this) {
+                            mActiveLockCount--;
+                        }
                     } catch (RemoteException ignore) {
                     }
                     mHeld = false;
@@ -783,6 +803,9 @@
                 if (mHeld) {
                     try {
                         mService.releaseWifiLock(mBinder);
+                        synchronized (WifiManager.this) {
+                            mActiveLockCount--;
+                        }
                     } catch (RemoteException ignore) {
                     }
                 }
@@ -877,6 +900,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) {
                     }
@@ -901,6 +932,9 @@
                 if (mHeld) {
                     try {
                         mService.releaseMulticastLock();
+                        synchronized (WifiManager.this) {
+                            mActiveLockCount--;
+                        }
                         mHeld = false;
                     } catch (RemoteException ignore) {
                     }