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) {
}