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