Merge change 6396 into donut

* changes:
  Add unit test for NeighboringCellInfo.
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..aefad66
--- /dev/null
+++ b/cmds/keystore/certtool.h
@@ -0,0 +1,91 @@
+/*
+**
+** 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"
+
+#define CERT_NAME_LEN (2 * MAX_KEY_NAME_LENGTH + 2)
+
+/*
+ * 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(const char *certname, unsigned char *value, int *size)
+{
+    int count, fd, ret = -1;
+    LPC_MARSHAL cmd;
+    char delimiter[] = "_";
+    char *namespace, *keyname;
+    char *context = NULL;
+    char cname[CERT_NAME_LEN];
+
+    if ((certname == NULL) || (value == NULL)) {
+        LOGE("get_cert: certname or value is null\n");
+        return -1;
+    }
+
+    if (strlcpy(cname, certname, CERT_NAME_LEN) >= CERT_NAME_LEN) {
+        LOGE("get_cert: keyname is too long\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(cname, 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..66edd56
--- /dev/null
+++ b/cmds/keystore/keymgmt.c
@@ -0,0 +1,372 @@
+/*
+** 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 (strlen(new_pass) < MIN_PASSWD_LENGTH) 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 (state != UNLOCKED) {
+        LOGE("Can not list key with current state %d\n", state);
+        return -1;
+    }
+
+    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;
+        if (strlen(data) < MIN_PASSWD_LENGTH) 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..0e928db
--- /dev/null
+++ b/cmds/keystore/keymgmt.h
@@ -0,0 +1,82 @@
+/*
+** 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 MIN_PASSWD_LENGTH 8
+
+#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/keystore_get.h b/cmds/keystore/keystore_get.h
new file mode 100644
index 0000000..a7fd9a5
--- /dev/null
+++ b/cmds/keystore/keystore_get.h
@@ -0,0 +1,53 @@
+/*
+**
+** 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 __KEYSTORE_GET_H__
+#define __KEYSTORE_GET_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "certtool.h"
+
+/* This function is provided to native components to get values from keystore.
+ * Users are required to link against libcutils. If something goes wrong, NULL
+ * is returned. Otherwise it returns the value in dynamically allocated memory
+ * and sets the size if the pointer is not NULL. One can release the memory by
+ * calling free(). */
+static char *keystore_get(char *key, int *size)
+{
+    char buffer[MAX_KEY_VALUE_LENGTH];
+    char *value;
+    int length;
+
+    if (get_cert(key, (unsigned char *)buffer, &length) != 0) {
+        return NULL;
+    }
+    value = malloc(length + 1);
+    if (!value) {
+        return NULL;
+    }
+    memcpy(value, buffer, length);
+    value[length] = 0;
+    if (size) {
+        *size = length;
+    }
+    return value;
+}
+
+#endif
diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c
new file mode 100644
index 0000000..eac455e
--- /dev/null
+++ b/cmds/keystore/netkeystore.c
@@ -0,0 +1,410 @@
+/*
+** 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/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/include/utils/AssetManager.h b/include/utils/AssetManager.h
index c11429e..d8994e0 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -251,6 +251,9 @@
         Asset* getResourceTableAsset();
         Asset* setResourceTableAsset(Asset* asset);
 
+        ResTable* getResourceTable();
+        ResTable* setResourceTable(ResTable* res);
+        
         bool isUpToDate();
         
     protected:
@@ -265,6 +268,7 @@
         time_t mModWhen;
 
         Asset* mResourceTableAsset;
+        ResTable* mResourceTable;
 
         static Mutex gLock;
         static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
@@ -288,8 +292,11 @@
          */
         ZipFileRO* getZip(const String8& path);
 
-        Asset* getZipResourceTable(const String8& path);
-        Asset* setZipResourceTable(const String8& path, Asset* asset);
+        Asset* getZipResourceTableAsset(const String8& path);
+        Asset* setZipResourceTableAsset(const String8& path, Asset* asset);
+
+        ResTable* getZipResourceTable(const String8& path);
+        ResTable* setZipResourceTable(const String8& path, ResTable* res);
 
         // generate path, e.g. "common/en-US-noogle.zip"
         static String8 getPathName(const char* path);
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index eb4151a..93bca4a 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1580,6 +1580,7 @@
                  bool copyData=false);
     status_t add(Asset* asset, void* cookie,
                  bool copyData=false);
+    status_t add(ResTable* src);
 
     status_t getError() const;
 
diff --git a/libs/audioflinger/AudioBufferProvider.h b/libs/audioflinger/AudioBufferProvider.h
index 1a467c7..81c5c39 100644
--- a/libs/audioflinger/AudioBufferProvider.h
+++ b/libs/audioflinger/AudioBufferProvider.h
@@ -36,6 +36,8 @@
         };
         size_t frameCount;
     };
+
+    virtual ~AudioBufferProvider() {}
     
     virtual status_t getNextBuffer(Buffer* buffer) = 0;
     virtual void releaseBuffer(Buffer* buffer) = 0;
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 324111b..8a19fbd 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");
         }
     }
 }
@@ -1280,7 +1281,7 @@
     status_t lStatus;
     
     // Resampler implementation limits input sampling rate to 2 x output sampling rate.
-    if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) {
+    if (sampleRate > mSampleRate*2) {
         LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
         lStatus = BAD_VALUE;
         goto Exit;
@@ -1595,8 +1596,8 @@
                 new(mCblk) audio_track_cblk_t();
                 // clear all buffers
                 mCblk->frameCount = frameCount;
-                mCblk->sampleRate = (uint16_t)sampleRate;
-                mCblk->channels = (uint16_t)channelCount;
+                mCblk->sampleRate = sampleRate;
+                mCblk->channels = (uint8_t)channelCount;
                 if (sharedBuffer == 0) {
                     mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
                     memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
@@ -1619,8 +1620,8 @@
            new(mCblk) audio_track_cblk_t();
            // clear all buffers
            mCblk->frameCount = frameCount;
-           mCblk->sampleRate = (uint16_t)sampleRate;
-           mCblk->channels = (uint16_t)channelCount;
+           mCblk->sampleRate = sampleRate;
+           mCblk->channels = (uint8_t)channelCount;
            mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
            memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
            // Force underrun condition to avoid false underrun callback until first data is
@@ -1681,7 +1682,7 @@
 }
 
 int AudioFlinger::MixerThread::TrackBase::channelCount() const {
-    return mCblk->channels;
+    return (int)mCblk->channels;
 }
 
 void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
@@ -2266,12 +2267,6 @@
         goto Exit;
     }
 
-    if (sampleRate > MAX_SAMPLE_RATE) {
-        LOGE("Sample rate out of range");
-        lStatus = BAD_VALUE;
-        goto Exit;
-    }
-
     if (mAudioRecordThread == 0) {
         LOGE("Audio record thread not started");
         lStatus = NO_INIT;
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/libs/utils/Asset.cpp b/libs/utils/Asset.cpp
index 91203dd..23cb72d 100644
--- a/libs/utils/Asset.cpp
+++ b/libs/utils/Asset.cpp
@@ -582,11 +582,14 @@
     if ((((size_t)data)&0x3) == 0) {
         // We can return this directly if it is aligned on a word
         // boundary.
+        LOGV("Returning aligned FileAsset %p (%s).", this,
+                getAssetSource());
         return data;
     }
     // If not aligned on a word boundary, then we need to copy it into
     // our own buffer.
-    LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength);
+    LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
+            getAssetSource(), (int)mLength);
     unsigned char* buf = new unsigned char[mLength];
     if (buf == NULL) {
         LOGE("alloc of %ld bytes failed\n", (long) mLength);
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 4126bfb..5a05e6a 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -395,21 +395,41 @@
     const size_t N = mAssetPaths.size();
     for (size_t i=0; i<N; i++) {
         Asset* ass = NULL;
+        ResTable* sharedRes = NULL;
         bool shared = true;
         const asset_path& ap = mAssetPaths.itemAt(i);
         LOGV("Looking for resource asset in '%s'\n", ap.path.string());
         if (ap.type != kFileTypeDirectory) {
-            ass = const_cast<AssetManager*>(this)->
-                mZipSet.getZipResourceTable(ap.path);
-            if (ass == NULL) {
-                LOGV("loading resource table %s\n", ap.path.string());
+            if (i == 0) {
+                // The first item is typically the framework resources,
+                // which we want to avoid parsing every time.
+                sharedRes = const_cast<AssetManager*>(this)->
+                    mZipSet.getZipResourceTable(ap.path);
+            }
+            if (sharedRes == NULL) {
                 ass = const_cast<AssetManager*>(this)->
-                    openNonAssetInPathLocked("resources.arsc",
-                                             Asset::ACCESS_BUFFER,
-                                             ap);
-                if (ass != NULL && ass != kExcludedAsset) {
+                    mZipSet.getZipResourceTableAsset(ap.path);
+                if (ass == NULL) {
+                    LOGV("loading resource table %s\n", ap.path.string());
                     ass = const_cast<AssetManager*>(this)->
-                        mZipSet.setZipResourceTable(ap.path, ass);
+                        openNonAssetInPathLocked("resources.arsc",
+                                                 Asset::ACCESS_BUFFER,
+                                                 ap);
+                    if (ass != NULL && ass != kExcludedAsset) {
+                        ass = const_cast<AssetManager*>(this)->
+                            mZipSet.setZipResourceTableAsset(ap.path, ass);
+                    }
+                }
+                
+                if (i == 0 && ass != NULL) {
+                    // If this is the first resource table in the asset
+                    // manager, then we are going to cache it so that we
+                    // can quickly copy it out for others.
+                    LOGV("Creating shared resources for %s", ap.path.string());
+                    sharedRes = new ResTable();
+                    sharedRes->add(ass, (void*)(i+1), false);
+                    sharedRes = const_cast<AssetManager*>(this)->
+                        mZipSet.setZipResourceTable(ap.path, sharedRes);
                 }
             }
         } else {
@@ -420,13 +440,19 @@
                                          ap);
             shared = false;
         }
-        if (ass != NULL && ass != kExcludedAsset) {
+        if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) {
             if (rt == NULL) {
                 mResources = rt = new ResTable();
                 updateResourceParamsLocked();
             }
             LOGV("Installing resource asset %p in to table %p\n", ass, mResources);
-            rt->add(ass, (void*)(i+1), !shared);
+            if (sharedRes != NULL) {
+                LOGV("Copying existing resources for %s", ap.path.string());
+                rt->add(sharedRes);
+            } else {
+                LOGV("Parsing resources for %s", ap.path.string());
+                rt->add(ass, (void*)(i+1), !shared);
+            }
 
             if (!shared) {
                 delete ass;
@@ -1510,7 +1536,8 @@
 DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen;
 
 AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen)
-    : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL)
+    : mPath(path), mZipFile(NULL), mModWhen(modWhen),
+      mResourceTableAsset(NULL), mResourceTable(NULL)
 {
     //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
     mZipFile = new ZipFileRO;
@@ -1563,6 +1590,25 @@
     return mResourceTableAsset;
 }
 
+ResTable* AssetManager::SharedZip::getResourceTable()
+{
+    LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable);
+    return mResourceTable;
+}
+
+ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res)
+{
+    {
+        AutoMutex _l(gLock);
+        if (mResourceTable == NULL) {
+            mResourceTable = res;
+            return res;
+        }
+    }
+    delete res;
+    return mResourceTable;
+}
+
 bool AssetManager::SharedZip::isUpToDate()
 {
     time_t modWhen = getFileModDate(mPath.string());
@@ -1572,6 +1618,9 @@
 AssetManager::SharedZip::~SharedZip()
 {
     //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
+    if (mResourceTable != NULL) {
+        delete mResourceTable;
+    }
     if (mResourceTableAsset != NULL) {
         delete mResourceTableAsset;
     }
@@ -1627,7 +1676,7 @@
     return zip->getZip();
 }
 
-Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path)
+Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path)
 {
     int idx = getIndex(path);
     sp<SharedZip> zip = mZipFile[idx];
@@ -1638,7 +1687,7 @@
     return zip->getResourceTableAsset();
 }
 
-Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path,
+Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path,
                                                  Asset* asset)
 {
     int idx = getIndex(path);
@@ -1647,6 +1696,26 @@
     return zip->setResourceTableAsset(asset);
 }
 
+ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path)
+{
+    int idx = getIndex(path);
+    sp<SharedZip> zip = mZipFile[idx];
+    if (zip == NULL) {
+        zip = SharedZip::get(path);
+        mZipFile.editItemAt(idx) = zip;
+    }
+    return zip->getResourceTable();
+}
+
+ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path,
+                                                    ResTable* res)
+{
+    int idx = getIndex(path);
+    sp<SharedZip> zip = mZipFile[idx];
+    // doesn't make sense to call before previously accessing.
+    return zip->setResourceTable(res);
+}
+
 /*
  * Generate the partial pathname for the specified archive.  The caller
  * gets to prepend the asset root directory.
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 4a5063a..109f28d 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -1136,8 +1136,9 @@
 
 struct ResTable::Header
 {
-    Header() : ownedData(NULL), header(NULL) { }
+    Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL) { }
 
+    ResTable* const                 owner;
     void*                           ownedData;
     const ResTable_header*          header;
     size_t                          size;
@@ -1163,8 +1164,8 @@
 
 struct ResTable::Package
 {
-    Package(const Header* _header, const ResTable_package* _package)
-        : header(_header), package(_package) { }
+    Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
+        : owner(_owner), header(_header), package(_package) { }
     ~Package()
     {
         size_t i = types.size();
@@ -1174,10 +1175,14 @@
         }
     }
     
+    ResTable* const                 owner;
     const Header* const             header;
     const ResTable_package* const   package;
     Vector<Type*>                   types;
 
+    ResStringPool                   typeStrings;
+    ResStringPool                   keyStrings;
+    
     const Type* getType(size_t idx) const {
         return idx < types.size() ? types[idx] : NULL;
     }
@@ -1188,13 +1193,16 @@
 // table that defined the package); the ones after are skins on top of it.
 struct ResTable::PackageGroup
 {
-    PackageGroup(const String16& _name, uint32_t _id)
-        : name(_name), id(_id), typeCount(0), bags(NULL) { }
+    PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
+        : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { }
     ~PackageGroup() {
         clearBagCache();
         const size_t N = packages.size();
         for (size_t i=0; i<N; i++) {
-            delete packages[i];
+            Package* pkg = packages[i];
+            if (pkg->owner == owner) {
+                delete pkg;
+            }
         }
     }
 
@@ -1225,15 +1233,17 @@
         }
     }
     
+    ResTable* const                 owner;
     String16 const                  name;
     uint32_t const                  id;
     Vector<Package*>                packages;
+    
+    // This is for finding typeStrings and other common package stuff.
+    Package*                        basePackage;
 
-    // Taken from the root package.
-    ResStringPool                   typeStrings;
-    ResStringPool                   keyStrings;
+    // For quick access.
     size_t                          typeCount;
-
+    
     // Computed attribute bags, first indexed by the type and second
     // by the entry in that type.
     bag_set***                      bags;
@@ -1560,11 +1570,36 @@
     return add(data, size, cookie, asset, copyData);
 }
 
+status_t ResTable::add(ResTable* src)
+{
+    mError = src->mError;
+    mParams = src->mParams;
+    
+    for (size_t i=0; i<src->mHeaders.size(); i++) {
+        mHeaders.add(src->mHeaders[i]);
+    }
+    
+    for (size_t i=0; i<src->mPackageGroups.size(); i++) {
+        PackageGroup* srcPg = src->mPackageGroups[i];
+        PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
+        for (size_t j=0; j<srcPg->packages.size(); j++) {
+            pg->packages.add(srcPg->packages[j]);
+        }
+        pg->basePackage = srcPg->basePackage;
+        pg->typeCount = srcPg->typeCount;
+        mPackageGroups.add(pg);
+    }
+    
+    memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
+    
+    return mError;
+}
+
 status_t ResTable::add(const void* data, size_t size, void* cookie,
                        Asset* asset, bool copyData)
 {
     if (!data) return NO_ERROR;
-    Header* header = new Header;
+    Header* header = new Header(this);
     header->index = mHeaders.size();
     header->cookie = cookie;
     mHeaders.add(header);
@@ -1682,10 +1717,12 @@
     N = mHeaders.size();
     for (size_t i=0; i<N; i++) {
         Header* header = mHeaders[i];
-        if (header->ownedData) {
-            free(header->ownedData);
+        if (header->owner == this) {
+            if (header->ownedData) {
+                free(header->ownedData);
+            }
+            delete header;
         }
-        delete header;
     }
 
     mPackageGroups.clear();
@@ -1728,8 +1765,8 @@
 
         outName->package = grp->name.string();
         outName->packageLen = grp->name.size();
-        outName->type = grp->typeStrings.stringAt(t, &outName->typeLen);
-        outName->name = grp->keyStrings.stringAt(
+        outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
+        outName->name = grp->basePackage->keyStrings.stringAt(
             dtohl(entry->key.index), &outName->nameLen);
         return true;
     }
@@ -2331,13 +2368,13 @@
             continue;
         }
 
-        const ssize_t ti = group->typeStrings.indexOfString(type, typeLen);
+        const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
         if (ti < 0) {
             TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
             continue;
         }
 
-        const ssize_t ei = group->keyStrings.indexOfString(name, nameLen);
+        const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
         if (ei < 0) {
             TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
             continue;
@@ -3630,25 +3667,36 @@
     PackageGroup* group = NULL;
     uint32_t id = dtohl(pkg->id);
     if (id != 0 && id < 256) {
+    
+        package = new Package(this, header, pkg);
+        if (package == NULL) {
+            return (mError=NO_MEMORY);
+        }
+        
         size_t idx = mPackageMap[id];
         if (idx == 0) {
             idx = mPackageGroups.size()+1;
 
             char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
             strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
-            group = new PackageGroup(String16(tmpName), id);
+            group = new PackageGroup(this, String16(tmpName), id);
             if (group == NULL) {
+                delete package;
                 return (mError=NO_MEMORY);
             }
 
-            err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+            err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
                                            header->dataEnd-(base+dtohl(pkg->typeStrings)));
             if (err != NO_ERROR) {
+                delete group;
+                delete package;
                 return (mError=err);
             }
-            err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+            err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
                                           header->dataEnd-(base+dtohl(pkg->keyStrings)));
             if (err != NO_ERROR) {
+                delete group;
+                delete package;
                 return (mError=err);
             }
 
@@ -3657,6 +3705,8 @@
             if (err < NO_ERROR) {
                 return (mError=err);
             }
+            group->basePackage = package;
+            
             mPackageMap[id] = (uint8_t)idx;
         } else {
             group = mPackageGroups.itemAt(idx-1);
@@ -3664,10 +3714,6 @@
                 return (mError=UNKNOWN_ERROR);
             }
         }
-        package = new Package(header, pkg);
-        if (package == NULL) {
-            return (mError=NO_MEMORY);
-        }
         err = group->packages.add(package);
         if (err < NO_ERROR) {
             return (mError=err);